Difference between revisions of "Float2Hex"

From Second Life Wiki
Jump to navigation Jump to search
m (<lsl> tag to <source>)
 
(32 intermediate revisions by 3 users not shown)
Line 1: Line 1:
{{LSL Header}}
{{LSL Header|ml=*}}
<div style="float:right">__TOC__</div>
<div style="float:right">__TOC__</div>
==About==
==About==
Use to encode floats in hex notation, minimal overhead, does not introduce errors. No special decoder needed. Try it.<br /><br /> Use this instead of <tt>(string)</tt> when converting floats to strings.
Use to encode floats in hex notation, minimal overhead, does not introduce errors. No special decoder needed. Try it.<br /><br /> Use this instead of <tt>(string)</tt> when converting floats to strings.


LSL <tt>(float)</tt> typecast supports [http://www.redhat.com/docs/manuals/enterprise/RHEL-4-Manual/gcc/hex-floats.html C99 hex floats]. This is good because with hex you can store floats without the need for a special decoder (just use <tt>(float)</tt>) and since hex is a power of 2 format, floats can be stored without any loss or rounding errors.
LSL <tt>(float)</tt> typecast supports [http://gcc.gnu.org/onlinedocs/gcc/Hex-Floats.html C99 hex floats]. This is good because with hex you can store floats without the need for a special decoder (just use <tt>(float)</tt>) 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) [http://forums.secondlife.com/showthread.php?t=28006 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.
A similar function (also by [[User:Strife Onizuka|me]]) [[Float2Sci|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 for large and small numbers (2 seconds). Finally some optical approaches were used to do the conversion which sped up large numbers. While accurate, the function was slow and used a lot 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.
This function is much faster, it requires about 300 instructions with little deviation from that number (unless the input is zero, which takes about 20 instructions). In the scheme of things it is not too costly. 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. A built in function would have the benefit of direct access to the memory that holds the float, much of the math performed here could be eliminated (see [[User:Strife_Onizuka/Float_Functions#FUI2HexFloat]]).


===Update===
===Update===


New version is about 5% faster and uses 24 fewer bytes. Removed the loop that performed the shift and replaced it with some smart math. Only downside is it uses one more variable.
*Full and seamless support for doubles.
*Better handling of rounding errors in the exponent (removes need for boundary checking)


==Float2Hex==
==Float2Hex==


<pre>
===Double Unsafe===
string hexc="0123456789ABCDEF";//faster
Fast & accurate in LSO and Mono but will not return full double precision in [[LSLEditor]]. LSLEditor is a bit of a moving target anyway so best not to worry about it.
 
 
<source lang="lsl2">string hexc="0123456789ABCDEF";//faster


string Float2Hex(float input)
string Float2Hex(float input)// Doubles Unsupported, LSO Safe, Mono Safe
{// Copyright Strife Onizuka, 2006, LGPL, http://www.gnu.org/copyleft/lesser.html
{// Copyright Strife Onizuka, 2006-2007, LGPL, http://www.gnu.org/copyleft/lesser.html or (cc-by) http://creativecommons.org/licenses/by/3.0/
     if((integer)input != 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
     {//this also keeps zero from hanging the zero stripper.
     {
        string str = (string)input;
        if(!~llSubStringIndex(str, ".")) return str; //NaN and Infinities, it's quick, it's easy.
         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 / (float)("0x1p"+(string)(exponent -= (exponent == 128)))) * 0x1000000);//shift up into integer range
 
         integer index = (integer)(llLog(mantissa ^ (mantissa & (~-(mantissa)))) / 0.69314718055994530941723212145818);//index of first 'on' bit
         integer mantissa = (integer)((unsigned / (float)("0x1p"+(string)(exponent -= ((exponent >> 31) | 1)))) * 0x4000000);//shift up into integer range
         string str = "p" + (string)((exponent += index) - 24);
         integer index = (integer)(llLog(mantissa & -mantissa) / 0.69314718055994530941723212145818);//index of first 'on' bit
         str = "p" + (string)(exponent + index - 26);
         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;
         return "0x" + str;
         return "0x" + str;
     }//integers pack well so anything that qualifies as an integer we dump as such, supports netative zero
     }//integers pack well so anything that qualifies as an integer we dump as such, supports negative zero
     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
}
}</source>
</pre>
 
===Safe===
This version isn't as fast but will work in LSLEditor, LSO and Mono.
<source lang="lsl2">string hexc="0123456789ABCDEF";//faster
 
string Float2Hex(float input)// Supports Doubles, 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
    {
        string str = (string)input;
        if(!~llSubStringIndex(str, ".")) return str; //NaN and Infinities, it's quick, it's easy.
        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 / (float)("0x1p"+(string)(exponent -= ((exponent >> 31) | 1)))) * 0x4000000));
        integer index = (integer)(llLog(mantissa_a & -mantissa_a) / 0.69314718055994530941723212145818);//index of first 'on' bit
        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 precision environments
            str = "p" + (string)(exponent + (index = (integer)(llLog(mantissa_b & -mantissa_b) / 0.69314718055994530941723212145818)) - 54);
            mantissa_b = (mantissa_b >> index) | ((mantissa_a << (28 - index)) & 0x0FFFFFFF);//mask it so we can shift check
            exponent = -7 * !!(mantissa_a >> index);// 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 negative zero
    return llDeleteSubString((string)input,-7,-1);//trim off the float portion, return an integer
}</source>
 
===Overkill===
This version will eat up as much precision as the hardware supports
 
<source lang="lsl2">string hexc="0123456789ABCDEF";
 
string Float2Hex(float input)//Float width irrelevant, 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
    {
        string str = (string)input;
        if(!~llSubStringIndex(str, ".")) return str; //NaN and Infinities, it's quick, it's easy.
        float unsigned = llFabs(input);//logs don't work on negatives.
        integer exponent = llFloor((llLog(unsigned) / 0.69314718055994530941723212145818));//floor(log2(b)) + rounding error
        unsigned /= (float)("0x1p"+(string)(exponent -= ((exponent >> 31) | 1)));
        integer count = -1;
        integer group = 0;
        list mantissa = [];
        do{
            count = -~count;
            mantissa += group = (integer)(unsigned *= 0x10000000);
        }while((unsigned -= group));
        integer index = (integer)(llLog(group & -group) / 0.69314718055994530941723212145818);//index of first 'on' bit
        str = "p" + (string)(exponent + index + (28 * ~count));//final exponent for single or simple double
        mantissa += 0;
        do{
            exponent = -7 * !!count;
            integer value = group >> index;
            value = value | (((group = llList2Integer(mantissa, (count = ~-count))) << (28 - index)) & 0x0FFFFFFF);
            do//if this is not the last value the loop must run 7 times; otherwise run as many times as needed
                str = llGetSubString(hexc, 15 & value, 15 & value) + str;
            while((value = (value >> 4)) | ((exponent = -~exponent) & 0x80000000));//dodge bugs in LSLEditor
        }while(~count);
        if(input < 0)
            return "-0x" + str;
        return "0x" + str;
    }//integers pack well so anything that qualifies as an integer we dump as such, supports negative zero
    return llDeleteSubString((string)input,-7,-1);//trim off the float portion, return an integer
}</source>
 


<br /> Usage:<br />
===Usage===


<pre>
<source lang="lsl2">
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
</pre>
</source>


<br /> Helper Functions:<br />
===Helper Functions===


<pre>
<source lang="lsl2">
string Rot2Hex(rotation a)
string Rot2Hex(rotation a)
{
{
Line 63: Line 148:


string DumpList2String(list input, string seperator)// for csv use ", " as the seperator.
string DumpList2String(list input, string seperator)// for csv use ", " as the seperator.
{
{// LSLEditor Unsafe, LSO Safe, Mono Safe
     integer b = (input != []);
     integer b = (input != []);
     string c;
     string c;
Line 73: Line 158:
         if((e = llGetListEntryType(input,--b)) == TYPE_FLOAT)
         if((e = llGetListEntryType(input,--b)) == TYPE_FLOAT)
             d=Float2Hex(llList2Float(input,b));
             d=Float2Hex(llList2Float(input,b));
         else if(e == TYPE ROTATION)
         else if(e == TYPE_ROTATION)
             d=Rot2Hex(llList2Rot(input,b));
             d=Rot2Hex(llList2Rot(input,b));
         else if(e == TYPE_VECTOR)
         else if(e == TYPE_VECTOR)
Line 87: Line 172:
     return (c=d) + c;
     return (c=d) + c;
}
}
</pre>
</source>


<br /> How it works:<br />
=== How it works ===


<pre>
<source lang="lsl2">
string hexc="0123456789ABCDEF";//faster
string hexc="0123456789ABCDEF";//faster


string Float2Hex(float a)
string Float2Hex(float input)// LSLEditor Safe, LSO Safe, Mono Safe
{// Copyright Strife Onizuka, 2006, LGPL, http://www.gnu.org/copyleft/lesser.html
{// Copyright Strife Onizuka, 2006-2007, LGPL, http://www.gnu.org/copyleft/lesser.html or (cc-by) http://creativecommons.org/licenses/by/3.0/
    //If it's zero, the zero stripper will hang, lets avoid that
    //LL screwed up hex integers support in rotation & vector string typecasting
    if(a)
    if(input != (integer)input)
    {//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.
}
</pre>
 
<br /> This version is perfect, it is slightly slower. It properly handles the exponent bug. It's included for curiosities sake (and a bunch of work went into it and i didn't want to throw it away).<br />
 
<pre>
string Float2Hex(float a)
{// Copyright Strife Onizuka, 2006, LGPL, http://www.gnu.org/copyleft/lesser.html
    if(a)
     {
     {
         float b = llFabs(a);
        //Convert it to a string and see if it has a decimal in it, if it does not, it's a string.
         integer c = llFloor(llLog(b) / 0.69314718055994530941723212145818);//floor(log2(b))
        string str = (string)input;
         string f = "";
        if(!~llSubStringIndex(str, ".")) return str; //NaN and Infinities, it's quick, it's easy.
         if(c > 127) c = 127;
        //logs don't work on negatives.
         else if(c < -126) c = -126;
         float unsigned = llFabs(input);
         else c -= ((float)("0x1p"+(string)c) > b);
        //calculate the exponent, this value is approxomit and it unfortunately will be rounded towards the extreams
         integer d = ((integer)(b / (float)("0x.000002p"+(string)c)) & 0x7FFFFF) << 1;
         integer exponent = llFloor((llLog(unsigned) / 0.69314718055994530941723212145818));//floor(log2(b)) + rounding error
        integer m = 6;
         //adjust the exponent so it errors towards zero
        while(!(d & 0xf))
         //if we don't adjust it, it will crash the script on overflow (bad)
        {//strip extra zeros off before converting or they break "p"
        exponent -= ((exponent >> 31) | 1);
             d = d >> 4;
        //using the exponent calculate the upper part of the mantisa with a few extra bits
            --m;
        unsigned = (unsigned / llPow(2., exponent)) * 0x4000000;
         //store it to an integer
        integer mantissa_a = (integer)unsigned;
         //find the first turned on bit, this also corrects for the rounding errors in the exponent
        integer index = (integer)(llLog(mantissa_a & -mantissa_a) / 0.69314718055994530941723212145818);//index of first 'on' bit
        //write the final exponent for single or simple double
        string str = "p" + (string)(exponent + index - 26);
        //subtract the upper part of the mantisa from the mantisa so that we have the fpart left
         integer mantissa_b = (integer)((unsigned - mantissa_a) * 0x10000000);
        //check to see if there is an fpart
        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
            //there was an fpart so the shift and final exponenet are wrong
            index = (integer)(llLog(mantissa_b & -mantissa_b) / 0.69314718055994530941723212145818);
            //generate the new final exponent
            str = "p" + (string)(exponent + index - 54);
            //apply the shift and adds some bits from the upper part
            //mask it so it so we can shift check
            mantissa_b = (mantissa_b >> index) | ((mantissa_a << (28 - index)) & 0x0FFFFFFF);
            //use the shift checking exclusively if there is no upper part to the mantissa
            //we recycle exponent
            exponent = -7 * !!mantissa_a;
            //if there is an upper part to the mantissa then the loop must run 7 times; otherwise run as many times as needed (less then 7 maybe)
            //we do this by peaking at the sine on exponent
            do
                str = llGetSubString(hexc, 15 & mantissa_b, 15 & mantissa_b) + str;
             while((mantissa_b = (mantissa_b >> 4)) | ((exponent = -~exponent) & 0x80000000));//dodge bugs in LSLEditor
         }
         }
         do
         //mantissa_a will always be non-zero before shifting unless it is in the denormalized range.
         {
        //if it is double then after the shifting we can't be sure
             f = llGetSubString(hexc, 0xf & d, 0xf & d) + f;
        //we wouldn't want to pad the output with an extra zero by accident.
             d = d >> 4;
         if((mantissa_a = (mantissa_a >> index)))
         }while(--m);
             do
         if(a < 0)
                str = llGetSubString(hexc, 15 & mantissa_a, 15 & mantissa_a) + str;
             return "-0x"+(string)(b > 1.1754943508222875079687365372222e-38)+"."+f +"p"+(string)(c);
             while((mantissa_a = (mantissa_a >> 4)) );
         return "0x"+(string)(b > 1.1754943508222875079687365372222e-38)+"."+f +"p"+(string)(c);
         //Put the sign and 0x on
         if(input < 0)
             return "-0x" + str;
         return "0x" + str;
     }
     }
     return "0";//zero would hang the zero stripper.
     //integers pack well so anything that qualifies as an integer we dump as such, supports netative zero
}
    //trim off the float portion, return an integer
</pre>
    return llDeleteSubString((string)input,-7,-1);
 
}</source>
==Double2Hex==
 
==LongDouble2Hex==


{{LSLC|Library|Float2Hex}}
{{LSLC|Library|Float2Hex}}

Latest revision as of 19:23, 24 January 2015

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 for large and small numbers (2 seconds). Finally some optical approaches were used to do the conversion which sped up large numbers. While accurate, the function was slow and used a lot of memory.

This function is much faster, it requires about 300 instructions with little deviation from that number (unless the input is zero, which takes about 20 instructions). In the scheme of things it is not too costly. 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. A built in function would have the benefit of direct access to the memory that holds the float, much of the math performed here could be eliminated (see User:Strife_Onizuka/Float_Functions#FUI2HexFloat).

Update

  • Full and seamless support for doubles.
  • Better handling of rounding errors in the exponent (removes need for boundary checking)

Float2Hex

Double Unsafe

Fast & accurate in LSO and Mono but will not return full double precision in LSLEditor. LSLEditor is a bit of a moving target anyway so best not to worry about it.


string hexc="0123456789ABCDEF";//faster

string Float2Hex(float input)// Doubles Unsupported, 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
    {
        string str = (string)input;
        if(!~llSubStringIndex(str, ".")) return str; //NaN and Infinities, it's quick, it's easy.
        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 / (float)("0x1p"+(string)(exponent -= ((exponent >> 31) | 1)))) * 0x4000000);//shift up into integer range
        integer index = (integer)(llLog(mantissa & -mantissa) / 0.69314718055994530941723212145818);//index of first 'on' bit
        str = "p" + (string)(exponent + index - 26);
        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 negative zero
    return llDeleteSubString((string)input,-7,-1);//trim off the float portion, return an integer
}

Safe

This version isn't as fast but will work in LSLEditor, LSO and Mono.

string hexc="0123456789ABCDEF";//faster

string Float2Hex(float input)// Supports Doubles, 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
    {
        string str = (string)input;
        if(!~llSubStringIndex(str, ".")) return str; //NaN and Infinities, it's quick, it's easy.
        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 / (float)("0x1p"+(string)(exponent -= ((exponent >> 31) | 1)))) * 0x4000000));
        integer index = (integer)(llLog(mantissa_a & -mantissa_a) / 0.69314718055994530941723212145818);//index of first 'on' bit
        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 precision environments
            str = "p" + (string)(exponent + (index = (integer)(llLog(mantissa_b & -mantissa_b) / 0.69314718055994530941723212145818)) - 54);
            mantissa_b = (mantissa_b >> index) | ((mantissa_a << (28 - index)) & 0x0FFFFFFF);//mask it so we can shift check
            exponent = -7 * !!(mantissa_a >> index);// 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 negative zero
    return llDeleteSubString((string)input,-7,-1);//trim off the float portion, return an integer
}

Overkill

This version will eat up as much precision as the hardware supports

string hexc="0123456789ABCDEF";

string Float2Hex(float input)//Float width irrelevant, 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
    {
        string str = (string)input;
        if(!~llSubStringIndex(str, ".")) return str; //NaN and Infinities, it's quick, it's easy.
        float unsigned = llFabs(input);//logs don't work on negatives.
        integer exponent = llFloor((llLog(unsigned) / 0.69314718055994530941723212145818));//floor(log2(b)) + rounding error
        unsigned /= (float)("0x1p"+(string)(exponent -= ((exponent >> 31) | 1)));
        integer count = -1;
        integer group = 0;
        list mantissa = [];
        do{
            count = -~count;
            mantissa += group = (integer)(unsigned *= 0x10000000);
        }while((unsigned -= group));
        integer index = (integer)(llLog(group & -group) / 0.69314718055994530941723212145818);//index of first 'on' bit
        str = "p" + (string)(exponent + index + (28 * ~count));//final exponent for single or simple double
        mantissa += 0;
        do{
            exponent = -7 * !!count;
            integer value = group >> index;
            value = value | (((group = llList2Integer(mantissa, (count = ~-count))) << (28 - index)) & 0x0FFFFFFF);
            do//if this is not the last value the loop must run 7 times; otherwise run as many times as needed
                str = llGetSubString(hexc, 15 & value, 15 & value) + str;
            while((value = (value >> 4)) | ((exponent = -~exponent) & 0x80000000));//dodge bugs in LSLEditor
        }while(~count);
        if(input < 0)
            return "-0x" + str;
        return "0x" + str;
    }//integers pack well so anything that qualifies as an integer we dump as such, supports negative zero
    return llDeleteSubString((string)input,-7,-1);//trim off the float portion, return an integer
}


Usage

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

Helper Functions

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.
{// LSLEditor Unsafe, LSO Safe, Mono Safe
    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;
}

How it works

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/
    //LL screwed up hex integers support in rotation & vector string typecasting
    if(input != (integer)input)
    {
        //Convert it to a string and see if it has a decimal in it, if it does not, it's a string.
        string str = (string)input;
        if(!~llSubStringIndex(str, ".")) return str; //NaN and Infinities, it's quick, it's easy.
        //logs don't work on negatives.
        float unsigned = llFabs(input);
        //calculate the exponent, this value is approxomit and it unfortunately will be rounded towards the extreams
        integer exponent = llFloor((llLog(unsigned) / 0.69314718055994530941723212145818));//floor(log2(b)) + rounding error
        //adjust the exponent so it errors towards zero
        //if we don't adjust it, it will crash the script on overflow (bad)
        exponent -= ((exponent >> 31) | 1);
        //using the exponent calculate the upper part of the mantisa with a few extra bits
        unsigned = (unsigned / llPow(2., exponent)) * 0x4000000;
        //store it to an integer
        integer mantissa_a = (integer)unsigned;
        //find the first turned on bit, this also corrects for the rounding errors in the exponent
        integer index = (integer)(llLog(mantissa_a & -mantissa_a) / 0.69314718055994530941723212145818);//index of first 'on' bit
        //write the final exponent for single or simple double
        string str = "p" + (string)(exponent + index - 26);
        //subtract the upper part of the mantisa from the mantisa so that we have the fpart left
        integer mantissa_b = (integer)((unsigned - mantissa_a) * 0x10000000);
        //check to see if there is an fpart
        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
            //there was an fpart so the shift and final exponenet are wrong
            index = (integer)(llLog(mantissa_b & -mantissa_b) / 0.69314718055994530941723212145818);
            //generate the new final exponent
            str = "p" + (string)(exponent + index - 54);
            //apply the shift and adds some bits from the upper part
            //mask it so it so we can shift check
            mantissa_b = (mantissa_b >> index) | ((mantissa_a << (28 - index)) & 0x0FFFFFFF);
            //use the shift checking exclusively if there is no upper part to the mantissa
            //we recycle exponent 
            exponent = -7 * !!mantissa_a;
            //if there is an upper part to the mantissa then the loop must run 7 times; otherwise run as many times as needed (less then 7 maybe)
            //we do this by peaking at the sine on exponent
            do
                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 it is double then after the shifting we can't be sure
        //we wouldn't want to pad the output with an extra zero by accident.
        if((mantissa_a = (mantissa_a >> index)))
            do
                str = llGetSubString(hexc, 15 & mantissa_a, 15 & mantissa_a) + str;
            while((mantissa_a = (mantissa_a >> 4)) );
        //Put the sign and 0x on
        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
    //trim off the float portion, return an integer
    return llDeleteSubString((string)input,-7,-1);
}