Difference between revisions of "User:Strife Onizuka/Float Functions"

From Second Life Wiki
Jump to navigation Jump to search
m (Then (consecutive time) vs than (comparison))
Line 1: Line 1:
{{LSL Header}}{{LSLC|User-Defined_Functions}}
{{LSL Header}}{{LSLC|User-Defined_Functions}}
==Float {{LSL_VR|-Union-}} Integer==
==Float {{LSL_VR|-Union-}} Integer==
I'd like to thank {{User|Pedro Oval} for their help with these functions.
<lsl>
<lsl>
integer fui(float a)//Mono Safe, LSO Safe, Doubles Unsupported, LSLEditor Unsafe
integer fui(float a)//Mono Safe, LSO Safe, Doubles Unsupported, LSLEditor Unsafe
{//union float to integer
{//union float to integer
     if((a)){//is it greater than or less than zero?
     if((a)){//is it nonzero?
         integer b = (a < 0) << 31;//the sign, but later this variable is reused to store the shift
         integer b = 0x80000000 * (a < 0);//the sign
         if((a = llFabs(a)) < 2.3509887016445750159374730744445e-38)//Denormalized range check & last stride of normalized range
         if((a = llFabs(a)) < 2.3509887016445750159374730744445e-38)//Denormalized range check & last stride of normalized range
             return b | (integer)(a / 1.4012984643248170709237295832899e-45);//the math overlaps; saves cpu time.
             return b | (integer)(a / 1.4012984643248170709237295832899e-45);//the math overlaps; saves cpu time.
         if(a > 3.4028234663852885981170418348452e+38)//Round up to infinity
         if(a > 3.4028234663852885981170418348452e+38)//Round up to infinity
             return b | 0x7F800000;//Positive or negative infinity
             return b | 0x7F800000;//Positive or negative infinity
         integer c = llFloor((llLog(a) / 0.69314718055994530941723212145818));//extremes will error towards extremes. following yuch corrects it.
         if(a > 1.4012984643248170709237295832899e-45){//It should at this point, except if it's NaN
        return (0x7FFFFF & (integer)(a * (0x1000000 >> b))) | (((c + 126 + (b = ((integer)a - (3 <= (a /= (float)("0x1p"+(string)(c -= ((c >> 31) | 1)))))))) << 23 ) | b);
            integer c = ~-llFloor(llLog(a) * 1.4426950408889634073599246810019);//extremes will error towards extremes. following yuch corrects it
    }//the previous requires a lot of unwinding to understand it.
            return b | (0x7FFFFF & (integer)(a * (0x1000000 >> c))) | ((126 + (c = ((integer)a - (3 <= (a *= llPow(2, -c))))) + c) * 0x800000);
    if(a == 0)//Just because it's not greater than or less than zero doesn't mean it's non-zero.
        }//the previous requires a lot of unwinding to understand it.
        return ((string)a == (string)(-0.0)) << 31;//for grins, detect the sign on zero. it's not pretty but it works.
        return 0x7FC00000;//NaN time! We have no way to tell NaN's apart so lets just choose one.
    //Mono does not support indeterminates so I'm not going to worry about it.
    }//Mono does not support indeterminates so I'm not going to worry about them.
    return 0x7FFFFFFF;//NaN time! We have no way to tell NaN's apart so lets just choose one.
    return 0x80000000 * ((string)a == "-0.000000");//for grins, detect the sign on zero. it's not pretty but it works.
}
}


float iuf(integer a)
float iuf(integer a)
{//union integer to float
{//union integer to float
     if((a & 0x7F800000) == 0x7F800000)
     if(!(0x7F800000 & ~a))
         return (1 | (a >> 31)) * (float)llList2String(["NaN","Infinity"], !(a & 0x7FFFFF));
         return (float)llGetSubString("-infnan", 3 * ~!(a & 0x7FFFFF), ~a >> 31);
     return ((float)("0x1p"+(string)((a | !a) - 150))) * ((!!(a = (0xff & (a >> 23))) << 23) | ((a & 0x7fffff))) * (1 | (a >> 31));
     return llPow(2, (a | !a) + 0xffffff6a) * (((!!(a = (0xff & (a >> 23)))) * 0x800000) | (a & 0x7fffff)) * (1 | (a >> 31));
}//will crash if the raw exponent == 0xff; reason for crash deviates from float standard; though a crash is warranted.
}
</lsl>
</lsl>


Line 32: Line 33:
string fuis(float a){//float union to base64ed integer
string fuis(float a){//float union to base64ed integer
     if((a)){//is it greater than or less than zero?
     if((a)){//is it greater than or less than zero?
         integer b = (a < 0) << 31;//the sign, but later this variable is reused to store the shift
         integer b = (a < 0) * 0x80000000;//the sign
         if((a = llFabs(a)) < 2.3509887016445750159374730744445e-38)//Denormalized range check & last stride of normalized range
         if((a = llFabs(a)) < 2.3509887016445750159374730744445e-38)//Denormalized range check & last stride of normalized range
             b = b | (integer)(a / 1.4012984643248170709237295832899e-45);//the math overlaps; saves cpu time.
             b = b | (integer)(a / 1.4012984643248170709237295832899e-45);//the math overlaps; saves cpu time.
         else if(a > 3.4028234663852885981170418348452e+38)//Round up to infinity
         else if(a > 3.4028234663852885981170418348452e+38)//Round up to infinity
             b = b | 0x7F800000;//Positive or negative infinity
             b = b | 0x7F800000;//Positive or negative infinity
         else
         else if(a > 1.4012984643248170709237295832899e-45){//It should at this point, except if it's NaN
        {
             integer c = ~-llFloor(llLog(a) * 1.4426950408889634073599246810019);//extremes will error towards extremes. following yuch corrects it
             integer c = llFloor(llLog(a) / 0.69314718055994530941723212145818);//extremes will error towards extremes. following yuch corrects it.
             b = b | (0x7FFFFF & (integer)(a * (0x1000000 >> c))) | ((126 + (c = ((integer)a - (3 <= (a *= llPow(2, -c))))) + c) * 0x800000);
             b = (0x7FFFFF & (integer)(a * (0x1000000 >> b))) | (((c + 126 + (b = ((integer)a - (3 <= (a /= (float)("0x1p"+(string)(c -= ((c >> 31) | 1)))))))) << 23 ) | b);
         }//the previous requires a lot of unwinding to understand it.
         }
        else
            b =  0x7FC00000;//NaN time! We have no way to tell NaN's apart so lets just choose one.
         return llGetSubString(llIntegerToBase64(b),0,5);
         return llGetSubString(llIntegerToBase64(b),0,5);
     }//for grins, detect the sign on zero. it's not pretty but it works. the previous requires a lot of unwinding to understand it.
     }//for grins, detect the sign on zero. it's not pretty but it works. the previous requires a lot of unwinding to understand it.
    if(a != 0)
     if((string)a == "-0.000000")
        return "f////w";
         return "gAAAAA";
     if((string)a == (string)(0.0))
     return "AAAAAA";
         return "AAAAAA";
     return "gAAAAA";
}
}


Line 54: Line 54:
{//base64ed integer union to float
{//base64ed integer union to float
     integer a = llBase64ToInteger(b);
     integer a = llBase64ToInteger(b);
     if((a & 0x7F800000) == 0x7F800000)
     if(!(0x7F800000 & ~a))
         return (1 | (a >> 31)) * (float)llList2String(["NaN","Infinity"], !(a & 0x7FFFFF));
         return (float)llGetSubString("-infnan", 3 * ~!(a & 0x7FFFFF), ~a >> 31);
     return ((float)("0x1p"+(string)((a | !a) - 150))) * ((!!(a = (0xff & (a >> 23))) << 23) | ((a & 0x7fffff))) * (1 | (a >> 31));
     return llPow(2, (a | !a) + 0xffffff6a) * (((!!(a = (0xff & (a >> 23)))) * 0x800000) | (a & 0x7fffff)) * (1 | (a >> 31));
}//will crash if the raw exponent == 0xff; reason for crash deviates from float standard; though a crash is warranted.
}
</lsl>
 
Of course if you have fui and iuf you can just wrap them and save some code duplication:
<lsl>
float siuf(string b) { return llGetSubString(llIntegerToBase64(iuf(b)),0,5); }
string fuis(float b) { return iuf(llBase64ToInteger(b)); }
</lsl>
</lsl>


Line 111: Line 117:
float i16uf(integer a)
float i16uf(integer a)
{//union short integer to half-precision float
{//union short integer to half-precision float
     if((a & 0x7C00) == 0x7C00)
     if(!(0x7C00 & ~a))
         return (1 | ((a << 16) >> 31)) * (float)llList2String(["NaN","Infinity"], !(a & 0x3FF));
         return (float)llGetSubString("-infnan", 3 * ~!(a & 0x3FF), ~((a << 16) >> 31));
     return 0.000000059604644775390625 * (1 << (a - !!a)) * ((!!(a = (0x1f & (a >> 10))) << 10) | ((a & 0x3ff))) * (1 | ((a << 16) >> 31));
     return 0.000000059604644775390625 * (1 << (a - !!a)) * ((!!(a = (0x1f & (a >> 10))) << 10) | ((a & 0x3ff))) * (1 | ((a << 16) >> 31));
}
}

Revision as of 23:49, 18 February 2013

Float <-Union-> Integer

I'd like to thank {{User|Pedro Oval} for their help with these functions. <lsl> integer fui(float a)//Mono Safe, LSO Safe, Doubles Unsupported, LSLEditor Unsafe {//union float to integer

   if((a)){//is it nonzero?
       integer b = 0x80000000 * (a < 0);//the sign
       if((a = llFabs(a)) < 2.3509887016445750159374730744445e-38)//Denormalized range check & last stride of normalized range
           return b | (integer)(a / 1.4012984643248170709237295832899e-45);//the math overlaps; saves cpu time.
       if(a > 3.4028234663852885981170418348452e+38)//Round up to infinity
           return b | 0x7F800000;//Positive or negative infinity
       if(a > 1.4012984643248170709237295832899e-45){//It should at this point, except if it's NaN
           integer c = ~-llFloor(llLog(a) * 1.4426950408889634073599246810019);//extremes will error towards extremes. following yuch corrects it
           return b | (0x7FFFFF & (integer)(a * (0x1000000 >> c))) | ((126 + (c = ((integer)a - (3 <= (a *= llPow(2, -c))))) + c) * 0x800000);
       }//the previous requires a lot of unwinding to understand it.
       return 0x7FC00000;//NaN time! We have no way to tell NaN's apart so lets just choose one.
   }//Mono does not support indeterminates so I'm not going to worry about them.
   return 0x80000000 * ((string)a == "-0.000000");//for grins, detect the sign on zero. it's not pretty but it works.

}

float iuf(integer a) {//union integer to float

   if(!(0x7F800000 & ~a))
       return (float)llGetSubString("-infnan", 3 * ~!(a & 0x7FFFFF), ~a >> 31);
   return llPow(2, (a | !a) + 0xffffff6a) * (((!!(a = (0xff & (a >> 23)))) * 0x800000) | (a & 0x7fffff)) * (1 | (a >> 31));

} </lsl>

Base64-Float

As a specialized mode of transport, this is faster than Float2Hex and just as lossless. <lsl> string fuis(float a){//float union to base64ed integer

   if((a)){//is it greater than or less than zero?
       integer b = (a < 0) * 0x80000000;//the sign
       if((a = llFabs(a)) < 2.3509887016445750159374730744445e-38)//Denormalized range check & last stride of normalized range
           b = b | (integer)(a / 1.4012984643248170709237295832899e-45);//the math overlaps; saves cpu time.
       else if(a > 3.4028234663852885981170418348452e+38)//Round up to infinity
           b = b | 0x7F800000;//Positive or negative infinity
       else if(a > 1.4012984643248170709237295832899e-45){//It should at this point, except if it's NaN
           integer c = ~-llFloor(llLog(a) * 1.4426950408889634073599246810019);//extremes will error towards extremes. following yuch corrects it
           b = b | (0x7FFFFF & (integer)(a * (0x1000000 >> c))) | ((126 + (c = ((integer)a - (3 <= (a *= llPow(2, -c))))) + c) * 0x800000);
       }//the previous requires a lot of unwinding to understand it.
       else 
           b =  0x7FC00000;//NaN time! We have no way to tell NaN's apart so lets just choose one.
       return llGetSubString(llIntegerToBase64(b),0,5);
   }//for grins, detect the sign on zero. it's not pretty but it works. the previous requires a lot of unwinding to understand it.
   if((string)a == "-0.000000")
       return "gAAAAA";
   return "AAAAAA";

}

float siuf(string b) {//base64ed integer union to float

   integer a = llBase64ToInteger(b);
   if(!(0x7F800000 & ~a))
       return (float)llGetSubString("-infnan", 3 * ~!(a & 0x7FFFFF), ~a >> 31);
   return llPow(2, (a | !a) + 0xffffff6a) * (((!!(a = (0xff & (a >> 23)))) * 0x800000) | (a & 0x7fffff)) * (1 | (a >> 31));

} </lsl>

Of course if you have fui and iuf you can just wrap them and save some code duplication: <lsl> float siuf(string b) { return llGetSubString(llIntegerToBase64(iuf(b)),0,5); } string fuis(float b) { return iuf(llBase64ToInteger(b)); } </lsl>

Half-Precision

Note: This truncates the value to make it half-precision, it does not round. <lsl> integer fui16t(float a)//Mono Safe, LSO Safe, Doubles Unsupported, LSLEditor Unsafe {//union half-precision float to short integer - truncates

   if((a)){//is it greater than or less than zero?
       integer b = (integer)(a < 0) << 15;//the sign, but later this variable is reused to store the shift
       if((a = llFabs(a)) < 0.0001220703125)//Denormalized range check & last stride of normalized range
           return b | (integer)(a * 16777216.0);//the math overlaps; saves cpu time.
       if(a >= 65536.0)//Round up to infinity
           return b | 0x7C00;//Positive or negative infinity
       integer c = llFloor((llLog(a) / 0.69314718055994530941723212145818)) + 14;//extremes will error towards extremes. following yuch corrects it.
       return (0x3FF & (integer)(a * (0x800 >> b))) | (((c + (b = ((integer)a - (3 <= (a *= (0.0000152587890625 * (0x40000000 >> c)))))) << 10 ) | b);
   }//the previous requires a lot of unwinding to understand it.
   if(a == 0)//Just because it's not greater than or less than zero doesn't mean it's non-zero.
       return ((string)a == (string)(-0.0)) << 15;//for grins, detect the sign on zero. it's not pretty but it works.
   //Mono does not support indeterminates so I'm not going to worry about it.
   return 0x7FFF;//NaN time! We have no way to tell NaN's apart so lets just choose one.

}

integer fui16(float a, integer rounding_mode)//Mono Safe, LSO Safe, Doubles Unsupported, LSLEditor Unsafe {//union half-precision float to short integer - with optional rounding

   if((a)){//is it greater than or less than zero?
       integer b = (integer)(a < 0) << 29;//the sign, but later this variable is reused to store the shift
       if((a = llFabs(a)) < 0.0001220703125)//Denormalized range check & last stride of normalized range
           b = b | (integer)(a * 274877906944.0);//the math overlaps; saves cpu time.
       else if(a >= 65536.0)//Round up to infinity
           b = b | 0x1F000000;//Positive or negative infinity
       else {
           integer c = llFloor((llLog(a) / 0.69314718055994530941723212145818)) + 14;//extremes will error towards extremes. following yuch corrects it.
           b = (0xFFFFFF & (integer)(a * (0x2000000 >> b))) | (((c + (b = ((integer)a - (3 <= (a *= (0.0000152587890625 * (0x40000000 >> c)))))) << 24 ) | b);
       }
       if(rounding_mode == 0)//truncate
           return b >> 14;
       if(rounding_mode == 1)//round to nearest, away from zero on tie.
           return (b >> 14) + ((b >> 13) & 1);
       if(rounding_mode == 2)//round to nearest, rounds to even on tie. This was a pain to work out how to code. Wikipedia says this is the default.
           return ((b >> 14) + ((b >> (13 + ((b & 0x3FFF) == 0x2000))) & 1);
       if(rounding_mode == 3)//round to +infinity
           return (b >> 14) + (((b << 2) >> 31) | 1) * !!(b & 0x3FFF);
       if(rounding_mode == 4)//round to -infinity
           return (b >> 14) - (((b << 2) >> 31) | 1) * !!(b & 0x3FFF);
   }//the previous requires a lot of unwinding to understand it.
   if(a == 0)//Just because it's not greater than or less than zero doesn't mean it's zero.
       return ((string)a == (string)(-0.0)) << 15;//for grins, detect the sign on zero. it's not pretty but it works.
   //Mono does not support indeterminates so I'm not going to worry about it.
   return 0x7FFF;//NaN time! We have no way to tell NaN's apart so lets just choose one.

}

float i16uf(integer a) {//union short integer to half-precision float

   if(!(0x7C00 & ~a))
       return (float)llGetSubString("-infnan", 3 * ~!(a & 0x3FF), ~((a << 16) >> 31));
   return 0.000000059604644775390625 * (1 << (a - !!a)) * ((!!(a = (0x1f & (a >> 10))) << 10) | ((a & 0x3ff))) * (1 | ((a << 16) >> 31));

} </lsl>

Float Distance

FloatDistance and FloatCompare are debugging functions. They allow you to test if float corruption is within acceptable bounds. Due to their contrived nature, they are rarely useful.

These functions work by assigning an integer value to each float value. These integer values are sequential, for every float value there is a unique integer value. FloatDistance looks at the difference of the two integer values and compares that with the max distance specified.

<lsl> integer FloatDistance(float a, float b, integer distance) {

   integer A = fui(a);
   integer B = fui(b);
   if((A ^ B) & 0x80000000)
       A = (A & 0x7fFfFfFf)-~(B & 0x7fFfFfFf);//If you want to treat -0 and 0 as the same number replace "-~" with "+"
   else
       A = llAbs(B - A);
   //distance is treated as if it were unsigned. If you do not want this, remove both " - 0x80000000"
   return (A - 0x80000000) <= (distance - 0x80000000);

} </lsl>

Float Compare

This function builds on the ideas set down in FloatDistance but changes how the distance is measured. I wrote this function before FloatDistance and didn't write any more documentation about it than what you see as comments. I'm not really keen on writing any for it now.

The idea is that if the values are within "c" range of each other than they are considered equal and zero is returned. Otherwise it returns the sign of (a - b).

<lsl>integer FloatCompare(float a, float b, integer c) {//compare floats and allow for a margin of error, requires fui().

   if(a != b)//(c) Strife Onizuka 2006 
   {//they are not equal
       //First we convert the floats to integer form, as they would be in memory;
       integer a_i = fui(a);
       integer b_i = fui(b);
       integer a_e = (a_i >> 23) & 0xff;
       integer b_e = (b_i >> 23) & 0xff;
       if(!(a_e || b_e) || //to disable the +/- roll under support put a // just before the !
           ((a_i & 0x80000000) == (b_i & 0x80000000)))//sign match check
       {//start by getting and testing the difference, this is what limits c
           integer diff = a_e - b_e;//ugly is fast, basically, it gets the mantissa, sets the sign on the mantissa,
           if(diff >= -1 || diff <= 1)//shifts it depending on exponent, finally executes the test.
               if(llAbs(((((a_i & 0x7FFFFF) | (!!a_e << 23)) * ((a_i >> 31) | 1)) >> !~-diff) - 
                        ((((b_i & 0x7FFFFF) | (!!b_e << 23)) * ((b_i >> 31) | 1)) >> !~diff)) <= c)
                   jump out;
       }
       return (a > b) - (a < b);
   }
   @out;
   return 0;

}</lsl>

FUI2HexFloat

<lsl> //This implementation isn't meant to create the most compact hexfloat and makes no effort to. //It was designed to quickly produce an accurate hexfloat. string FUI2HexFloat(integer b) {//Dump FUI float-integer to a hex-float string

   string c = "";
   if(e ^ 127)
   {
       integer e = 0xff & (b >> 23);
       c = "p" + (string)((e | !e) - 127);
       integer f = 0xfffffe & (b << 1);
       if(f)
       {
           integer d = 0;
           integer g;
           while(!((f >> d) & 0xf))
               d+=4;
           for( ;d < 24; d += 4)
               c = llGetSubString(hexc, g = 0xf & (f >> d), g) + c;
       }
       c = "0x"+(string)(!!e) + "." + c;
   }
   else if(b & 0x7fffff)
       c = "NaN";
   else
       c = "Infinity";
   if(b & 0x80000000)
       c = "-" + c;
   return c;

}

string Float2Hex(float a) {//Another way to do Float2Hex, i wrote this for the heck of it; not because it's a good idea.

   return FUI2HexFloat(fui(a));

} </lsl>