Difference between revisions of "Float2Hex"
Line 15: | Line 15: | ||
==Float2Hex== | ==Float2Hex== | ||
When {{Jira|SVC-1342}} & {{Jira|SVC-1376}} are fixed these version will be updated. | |||
===Safe=== | ===Safe=== | ||
This version isn't as fast but | This version isn't as fast but will work in LSLEditor, LSO and Mono. | ||
< | <lsl> | ||
string hexc="0123456789ABCDEF";//faster | string hexc="0123456789ABCDEF";//faster | ||
string Float2Hex(float input)// | string Float2Hex(float input)// LSLEditor Safe, LSO Safe, Mono Safe | ||
{// Copyright Strife Onizuka, 2006-2007, LGPL, http://www.gnu.org/copyleft/lesser.html or (cc-by) http://creativecommons.org/licenses/by/3.0/ | {// Copyright Strife Onizuka, 2006-2007, LGPL, http://www.gnu.org/copyleft/lesser.html or (cc-by) http://creativecommons.org/licenses/by/3.0/ | ||
if(input != (integer)input)//LL screwed up hex integers support in rotation & vector string typecasting | if(input != (integer)input)//LL screwed up hex integers support in rotation & vector string typecasting | ||
Line 26: | Line 28: | ||
float unsigned = llFabs(input);//logs don't work on negatives. | float unsigned = llFabs(input);//logs don't work on negatives. | ||
integer exponent = llFloor((llLog(unsigned) / 0.69314718055994530941723212145818));//floor(log2(b)) + rounding error | integer exponent = llFloor((llLog(unsigned) / 0.69314718055994530941723212145818));//floor(log2(b)) + rounding error | ||
integer | |||
integer index = (integer) | integer mantissa_a = (integer)(unsigned = ((unsigned / llPow(2., exponent += ~((exponent >> 31) & -2))) * 0x4000000)); | ||
string str = "p" + (string)(exponent + index - | integer index = (integer)(llLog(mantissa_a & -mantissa_a) / 0.69314718055994530941723212145818);//index of first 'on' bit | ||
mantissa = | string str = "p" + (string)(exponent + index - 26);//final exponent for single or simple double | ||
do | integer mantissa_b = (integer)((unsigned - mantissa_a) * 0x10000000); | ||
if(mantissa_b)//this code only gets activated if the lower bytes of the double mantissa are set | |||
{//this code won't get activated in single percission environments | |||
str = "p" + (string)(exponent + (index = (integer)(llLog(mantissa_b & -mantissa_b) / 0.69314718055994530941723212145818)) - 54);//final exponent | |||
mantissa_b = (mantissa_b >> index) | ((mantissa_a << (28 - index)) & 0x0FFFFFFF);//mask it so it so we can shift check | |||
exponent = -7 * !!mantissa_a;// use the shift checking exclusively if there is no mantissa_a | |||
do// if there is mantissa_a then the loop must run 7 times; otherwise run as many times as needed | |||
str = llGetSubString(hexc, 15 & mantissa_b, 15 & mantissa_b) + str; | |||
while((mantissa_b = (mantissa_b >> 4)) | ((exponent = -~exponent) & 0x80000000));//dodge bugs in LSLEditor | |||
}// mantissa_a will always be non-zero before shifting unless it is in the denormalized range. | |||
if((mantissa_a = (mantissa_a >> index)))//if it is double then after the shifting we can't be sure | |||
do//we wouldn't want to pad the output with an extra zero by accident. | |||
str = llGetSubString(hexc, 15 & mantissa_a, 15 & mantissa_a) + str; | |||
while((mantissa_a = (mantissa_a >> 4)) ); | |||
if(input < 0) | if(input < 0) | ||
return "-0x" + str; | return "-0x" + str; | ||
Line 39: | Line 53: | ||
return llDeleteSubString((string)input,-7,-1);//trim off the float portion, return an integer | return llDeleteSubString((string)input,-7,-1);//trim off the float portion, return an integer | ||
} | } | ||
</ | </lsl> | ||
=== | ===Double Unsafe=== | ||
Fast & accurate in LSO but will not | Fast & accurate in LSO and Mono but will not work properly in [[LSLEditor]]. | ||
< | <lsl> | ||
string hexc="0123456789ABCDEF";//faster | string hexc="0123456789ABCDEF";//faster | ||
string Float2Hex(float input)// | string Float2Hex(float input)// LSLEditor Unsafe, LSO Safe, Mono Safe | ||
{// Copyright Strife Onizuka, 2006-2007, LGPL, http://www.gnu.org/copyleft/lesser.html or (cc-by) http://creativecommons.org/licenses/by/3.0/ | {// Copyright Strife Onizuka, 2006-2007, LGPL, http://www.gnu.org/copyleft/lesser.html or (cc-by) http://creativecommons.org/licenses/by/3.0/ | ||
if(input != (integer)input)//LL screwed up hex integers support in rotation & vector string typecasting | if(input != (integer)input)//LL screwed up hex integers support in rotation & vector string typecasting | ||
Line 52: | Line 66: | ||
float unsigned = llFabs(input);//logs don't work on negatives. | float unsigned = llFabs(input);//logs don't work on negatives. | ||
integer exponent = llFloor((llLog(unsigned) / 0.69314718055994530941723212145818));//floor(log2(b)) + rounding error | integer exponent = llFloor((llLog(unsigned) / 0.69314718055994530941723212145818));//floor(log2(b)) + rounding error | ||
integer mantissa = (integer)((unsigned / ( | |||
integer index = (integer) | integer mantissa = (integer)((unsigned / llPow(2., exponent += ~((exponent >> 31) & -2))) * 0x4000000);//shift up into integer range | ||
string str = "p" + (string)(exponent + index - | integer index = (integer)(llLog(mantissa & -mantissa) / 0.69314718055994530941723212145818);//index of first 'on' bit | ||
string str = "p" + (string)(exponent + index - 25); | |||
mantissa = mantissa >> index; | mantissa = mantissa >> index; | ||
do | do | ||
str = llGetSubString(hexc, 15 & mantissa, 15 & mantissa) + str; | str = llGetSubString(hexc, 15 & mantissa, 15 & mantissa) + str; | ||
while(mantissa = mantissa >> 4); | while(mantissa = mantissa >> 4); | ||
if(input < 0) | if(input < 0) | ||
return "-0x" + str; | return "-0x" + str; | ||
Line 65: | Line 81: | ||
return llDeleteSubString((string)input,-7,-1);//trim off the float portion, return an integer | return llDeleteSubString((string)input,-7,-1);//trim off the float portion, return an integer | ||
} | } | ||
</ | </lsl> | ||
<br /> Usage:<br /> | <br /> Usage:<br /> | ||
< | <lsl> | ||
string a = Float2Hex(100.000000); // a == "100" | string a = Float2Hex(100.000000); // a == "100" | ||
string b = Float2Hex(14353.344727); // b == 0xE04561p-10 | string b = Float2Hex(14353.344727); // b == 0xE04561p-10 | ||
float c = (float)a;//c == 100.000000 | float c = (float)a;//c == 100.000000 | ||
float d = (float)b;//d == 14353.344727 | float d = (float)b;//d == 14353.344727 | ||
</ | </lsl> | ||
<br /> Helper Functions:<br /> | <br /> Helper Functions:<br /> | ||
< | <lsl> | ||
string Rot2Hex(rotation a) | string Rot2Hex(rotation a) | ||
{ | { | ||
Line 114: | Line 130: | ||
return (c=d) + c; | return (c=d) + c; | ||
} | } | ||
</ | </lsl> | ||
<br /> How it works:<br /> | <br /> How it works:<br /> | ||
< | <lsl> | ||
string hexc="0123456789ABCDEF";//faster | string hexc="0123456789ABCDEF";//faster | ||
Line 156: | Line 172: | ||
return "0";//zero would hang the zero stripper. | return "0";//zero would hang the zero stripper. | ||
} | } | ||
</ | </lsl> | ||
==Double2Hex== | ==Double2Hex== |
Revision as of 22:08, 31 January 2008
LSL Portal | Functions | Events | Types | Operators | Constants | Flow Control | Script Library | Categorized Library | Tutorials |
About
Use to encode floats in hex notation, minimal overhead, does not introduce errors. No special decoder needed. Try it.
Use this instead of (string) when converting floats to strings.
LSL (float) typecast supports C99 hex floats. This is good because with hex you can store floats without the need for a special decoder (just use (float)) and since hex is a power of 2 format, floats can be stored without any loss or rounding errors.
A similar function (also by me) Float to Scientific Notation, works much the same way (except it uses base 10). The trouble is it has to emulates higher precision math, in a scripting language this is a bad solution. Because of the base conversion using logs would introduce a huge accumulated error (read as: floats suck). Resulting in the need to do the shifting with a while loop. This wasn't good enough, even with 32 bits small numbers would still be corrupted by the shifting. An integer and a float (or in one rewrite two integers) were used, one to store the integer portion, and the other the float. This worked, but was slow as all get out for large and small numbers (2 seconds). Finaly some optical approches were used to do the conversion which sped up large numbers. While accurate, the function was slow and used alot of memory.
This function is much faster, it requires about 900 instructions with little deviation from that number. In the sceme of things it's not too costly (on a not too lagged sim that is about 0.12 seconds). There isn't much that can be done to reduce the number of instructions executed, unless LL wants to give us an llInteger2HexString or an llFloat2HexString.
Update
Auto adjust so doubles can be used instead of floats. The safe version should be Mono ready.
Float2Hex
When SVC-1342 & SVC-1376 are fixed these version will be updated.
Safe
This version isn't as fast but will work in LSLEditor, LSO and Mono. <lsl> string hexc="0123456789ABCDEF";//faster
string Float2Hex(float input)// LSLEditor Safe, LSO Safe, Mono Safe {// Copyright Strife Onizuka, 2006-2007, LGPL, http://www.gnu.org/copyleft/lesser.html or (cc-by) http://creativecommons.org/licenses/by/3.0/
if(input != (integer)input)//LL screwed up hex integers support in rotation & vector string typecasting {//this also keeps zero from hanging the zero stripper. float unsigned = llFabs(input);//logs don't work on negatives. integer exponent = llFloor((llLog(unsigned) / 0.69314718055994530941723212145818));//floor(log2(b)) + rounding error
integer mantissa_a = (integer)(unsigned = ((unsigned / llPow(2., exponent += ~((exponent >> 31) & -2))) * 0x4000000)); integer index = (integer)(llLog(mantissa_a & -mantissa_a) / 0.69314718055994530941723212145818);//index of first 'on' bit string str = "p" + (string)(exponent + index - 26);//final exponent for single or simple double integer mantissa_b = (integer)((unsigned - mantissa_a) * 0x10000000); if(mantissa_b)//this code only gets activated if the lower bytes of the double mantissa are set {//this code won't get activated in single percission environments str = "p" + (string)(exponent + (index = (integer)(llLog(mantissa_b & -mantissa_b) / 0.69314718055994530941723212145818)) - 54);//final exponent mantissa_b = (mantissa_b >> index) | ((mantissa_a << (28 - index)) & 0x0FFFFFFF);//mask it so it so we can shift check exponent = -7 * !!mantissa_a;// use the shift checking exclusively if there is no mantissa_a do// if there is mantissa_a then the loop must run 7 times; otherwise run as many times as needed str = llGetSubString(hexc, 15 & mantissa_b, 15 & mantissa_b) + str; while((mantissa_b = (mantissa_b >> 4)) | ((exponent = -~exponent) & 0x80000000));//dodge bugs in LSLEditor }// mantissa_a will always be non-zero before shifting unless it is in the denormalized range. if((mantissa_a = (mantissa_a >> index)))//if it is double then after the shifting we can't be sure do//we wouldn't want to pad the output with an extra zero by accident. str = llGetSubString(hexc, 15 & mantissa_a, 15 & mantissa_a) + str; while((mantissa_a = (mantissa_a >> 4)) );
if(input < 0) return "-0x" + str; return "0x" + str; }//integers pack well so anything that qualifies as an integer we dump as such, supports netative zero return llDeleteSubString((string)input,-7,-1);//trim off the float portion, return an integer
} </lsl>
Double Unsafe
Fast & accurate in LSO and Mono but will not work properly in LSLEditor. <lsl> string hexc="0123456789ABCDEF";//faster
string Float2Hex(float input)// LSLEditor Unsafe, LSO Safe, Mono Safe {// Copyright Strife Onizuka, 2006-2007, LGPL, http://www.gnu.org/copyleft/lesser.html or (cc-by) http://creativecommons.org/licenses/by/3.0/
if(input != (integer)input)//LL screwed up hex integers support in rotation & vector string typecasting {//this also keeps zero from hanging the zero stripper. float unsigned = llFabs(input);//logs don't work on negatives. integer exponent = llFloor((llLog(unsigned) / 0.69314718055994530941723212145818));//floor(log2(b)) + rounding error
integer mantissa = (integer)((unsigned / llPow(2., exponent += ~((exponent >> 31) & -2))) * 0x4000000);//shift up into integer range integer index = (integer)(llLog(mantissa & -mantissa) / 0.69314718055994530941723212145818);//index of first 'on' bit string str = "p" + (string)(exponent + index - 25); mantissa = mantissa >> index; do str = llGetSubString(hexc, 15 & mantissa, 15 & mantissa) + str; while(mantissa = mantissa >> 4);
if(input < 0) return "-0x" + str; return "0x" + str; }//integers pack well so anything that qualifies as an integer we dump as such, supports netative zero return llDeleteSubString((string)input,-7,-1);//trim off the float portion, return an integer
} </lsl>
Usage:
<lsl> string a = Float2Hex(100.000000); // a == "100" string b = Float2Hex(14353.344727); // b == 0xE04561p-10 float c = (float)a;//c == 100.000000 float d = (float)b;//d == 14353.344727 </lsl>
Helper Functions:
<lsl> string Rot2Hex(rotation a) {
return "<"+Float2Hex(a.x)+","+Float2Hex(a.y)+","+Float2Hex(a.z)+","+Float2Hex(a.s)+">";
}
string Vec2Hex(vector a) {
return "<"+Float2Hex(a.x)+","+Float2Hex(a.y)+","+Float2Hex(a.z)+">";
}
string DumpList2String(list input, string seperator)// for csv use ", " as the seperator. {
integer b = (input != []); string c; string d; integer e; if(b) { @top; if((e = llGetListEntryType(input,--b)) == TYPE_FLOAT) d=Float2Hex(llList2Float(input,b)); else if(e == TYPE ROTATION) d=Rot2Hex(llList2Rot(input,b)); else if(e == TYPE_VECTOR) d=Vec2Hex(llList2Vector(input,b)); else d=llList2String(input,b); if(b) { c = d + (c=seperator) + c; jump top; } } return (c=d) + c;
} </lsl>
How it works:
<lsl> string hexc="0123456789ABCDEF";//faster
string Float2Hex(float a) {// Copyright Strife Onizuka, 2006-2007, LGPL, http://www.gnu.org/copyleft/lesser.html
//If it's zero, the zero stripper will hang, lets avoid that if(a) {//we need to find the exponent, but to do that we need to take the log of the number (or run a while loop) float b = llFabs(a);//logs don't work on negatives. string f = "p";//This is a trade off, slightly slower (only slightly) for a couple bytes savings (10) //To get the exponent we need to round the integer portion down or the log2(b) // LSL doesn't have log2, so we use one of the laws of logs to get it from the natural log integer c = llFloor(llLog(b) / 0.69314718055994530941723212145818);//floor(log2(b)) //There is a rounding error in the exponent that causes it to round up, this usualy isn't a problem as the // exponent only is only used to shift the number. It becomes a problem when dealing with float max as // it will round up to 128; which will crash llPow. if(c > 127) c = 127;//catch fatal rounding error in exponent. //Q: why not do (b * llPow(2,-c))? A: because the range of floats are different at the top and bottom // at the top it's 2^127 at the bottom it's 2^-149; and if you tried to do this otherwise, // llPow would crash with an overflow. Then we multiply it by 2 ^ 24, floats only use 23 bits for the mantisa, // we cannot add it into c because we could overflow llPow; we correct the value of c later. // Instead of using llPow(2.0, c) we are using (float)("0x1p"+(string)c), it's 10% faster and 16 bytes lighter. integer d = (integer)((b / (float)("0x1p"+(string)c)) * 0x1000000);//shift up into integer range while(!(d & 0xf)) {//strip extra zeros off before converting or they break "p" //a catch22 of sorts, you cannot know exactly how far to shift it till after you have shifted it d = d >> 4; c+=4; //for each zero stripped we must adjust the exponent } do//convert to hex f = llGetSubString(hexc,15&d,15&d) + f; while(d = d >> 4); if(a < 0)//final formating & c adjustment (from making it an integer) return "-0x" + f +(string)(c - 24); return "0x" + f +(string)(c - 24); }//no point in this getting special formating. return "0";//zero would hang the zero stripper.
} </lsl>