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

From Second Life Wiki
Jump to navigation Jump to search
Line 211: Line 211:


::Kira Komarov
::Kira Komarov
:::Sorry didn't mean to get you riled with a short incomplete reply, I just ran out of time and didn't want to give you nothing. -- '''[[User:Strife_Onizuka|Strife]]''' <sup><small>([[User talk:Strife_Onizuka|talk]]|[[Special:Contributions/Strife_Onizuka|contribs]])</small></sup> 11:47, 24 May 2012 (PDT)
:I wrote the function in the course of examining floating point arithmetic drift. That is to say the steady corruption of the lowest bits due to arithmetic operations. The problem is corruption manifests itself in the values of the bits in the mantissa of the float, it can even result in the exponent being increased.
:I feel I need to explain how floats work so lets take a moment to go over that. However it's easier for me to provide code that replicates how it works than to put how it works into words.
:A float is made of three parts. This is how it is stored in memory. All operators take it in this for, perform their operation and return a value in this form. A float is just a collection of bits that have meaning given a specific context.
:#sign - 1 bit
:#exponent - 8 bits (unsigned)
:#mantissa - 23 bits (unsigned)
:The value of a float is calculated as follows:
<lsl>float value(integer sign, integer exponent, integer mantissa){
    //make the sign 1 or -1, zero maps to 1
    sign = 1 | -!!sign;
   
    //For the sake of simplicity we will ignore NaN and Infinity values.
   
    //normalized range
    if(exponent)
        return sign * (0x800000 + mantissa) * llPow(2.0, exponent - 150);
    //denormalized range
    return sign * mantissa * llPow(2.0, -149);
}</lsl>
:Now LSL is not C, in C you can take a float, do some hand waving and presto you can read the float value as if it were an integer value. That is to say, given the memory location of the float, read that location as if it were an integer. Since LSL does not let you do this (though there is nothing stopping you from doing this in LSO), I wrote a set of functions to approximate this. This is what FUI and IUF do (their only flaw is not properly handling <code>nan</code> and <code>infinity</code>). I wrote these functions of an LSL based LSO interpreter that I never finished. They are an evolutionary dead end a solution without a problem to solve.
:The problem is that as you perform certain arithmetic operations you get drift. The lowest bits of the mantissa do not match up with the math. That is to say, the way we as humans evaluate the operations is slightly different than the way the computer does. Take for example (1.0 / 10) * 10. As humans we would say that it is equal to 1. The computer on the other hand will say it's not equal to 1. This is because the result of (1.0 / 10) is an approximation, the precise value would a different way of storing the value.
:What this function does is let you compare the mantissa of the two floats. In it's most basic form it's <code>((mantissa_a - mantissa_b) > c)</code> (this simplification doesn't take into consideration that the exponents won't match, or that the sign of the return is based on the larger value).

Revision as of 11:47, 24 May 2012

What's the purpose of llFloatCompare?

Hi, I'm a bit confused by llFloatCompare, why include fui, iuf and FloatCompare in a script when you can just do it this way:

<lsl>

       float f_1 = 0.00000003;
       float f_2 = 0.00000002;
       if(llFabs(f_1) - llFabs(f_2) > 0 ) {
           llOwnerSay("f_1 > f_2");
           return;
       }
       if(llFabs(f_1 - f_2) == 0.0) {
           llOwnerSay("f_1 = f_2");
           return;
       }
       llOwnerSay("f_1 < f_2");

</lsl>

I have run a test in Second Life, using your FloatCompare function (because none of required functions even compile on OpenSim) and compared it to my method and the results are the same, even my simple method succeeds where your method fails. I used the following script:

<lsl> // [ bla bla bla ] // I've spared you the 40 lines of code and functions that your FloatCompare method requires.

default {

   state_entry() {
       float f_1 = 0.999993;
       float f_2 = 0.999992;
       if(llFabs(f_1) - llFabs(f_2) > 0 ) {
           llOwnerSay("Kira: f_1 > f_2");
           jump strife;
       }
       if(llFabs(f_1 - f_2) == 0.0) {
           llOwnerSay("Kira: f_1 = f_2");
           jump strife;
       }
       llOwnerSay("Kira: f_1 < f_2");

@strife;

       llOwnerSay("Strife: " + (string)FloatCompare(f_1, f_2, 1));
   }

} </lsl>


Test 1, Your method fails.

For <lsl> float f_1 = 0.9999993; float f_2 = 0.9999992; </lsl>

Output

Object: Kira: f_1 > f_2
Object: Strife: 0

They are not equal... What is this?


Test 2, Your method fails.

For: <lsl> float f_1 = 0.9999003; float f_2 = 0.9999002; </lsl>

Output

Object: Kira: f_1 > f_2
Object: Strife: 0

Still equal? WTH?


Test 3, We both succeed.

For: <lsl> float f_1 = 0.999993; float f_2 = 0.999992; </lsl>

Output

Object: Kira: f_1 > f_2
Object: Strife: 1

Test 4, We both fail.

<lsl> float f_1 = 0.000000000000000000000000000000000000000000002; float f_2 = 0.000000000000000000000000000000000000000000001; </lsl>

Output

Object: Kira: f_1 = f_2
Object: Strife: 0

Here are a few funny ones:

Test 5, My method works, your method just crashes with Math Error

<lsl> float f_1 = 3.402E+38; float f_2 = 3.403E+38; </lsl>

Output

Object: Kira: f_1 < f_2
Object: Object [script:New Script] Script run-time error
Object: Math Error

This one too:

Test 6, My method works, your method just crashes with Math Error

<lsl> float f_1 = 3.403E+38; float f_2 = 3.402E+38; </lsl>

Output

Object: Kira: f_1 > f_2
Object: Object [script:New Script] Script run-time error
Object: Math Error

Don't get me wrong, you provide no documentation on what those functions are doing (what they mean) so I don't know if I used them correctly...

  • Could you please clarify what they are supposed to do?
  • Why include this chunk of obfuscated garbage with magic numbers:

<lsl> integer fui(float a)//Mono Safe, LSO Safe, Doubles Unsupported, LSLEditor Unsafe {//union float to integer

   if((a)){//is it non zero?
       integer b = (a < 0) << 31;//the sign, but later this variable is reused to store the shift
       if((a = llFabs(a)) < 2.3509887016445750159374730744445e-38)//Denormalized range check & last stirde of normalized range
           return b | (integer)(a / 1.4012984643248170709237295832899e-45);//the math overlaps; saves cpu time.
       integer c = llFloor((llLog(a) / 0.69314718055994530941723212145818));//extremes will error towards extremes. following yuch corrects it.
       return (0x7FFFFF & (integer)(a * (0x1000000 >> b))) | (((c + 126 + (b = ((integer)a - (3 <= (a /= (float)("0x1p"+(string)(c -= ((c >> 31) | 1)))))))) << 23 ) | b);
   }//for grins, detect the sign on zero. it's not pretty but it works. the previous requires alot of unwinding to understand it.
   return ((string)a == (string)(-0.0)) << 31;

}

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

   return ((float)("0x1p"+(string)((a | !a) - 150))) * ((!!(a = (0xff & (a >> 23))) << 23) | ((a & 0x7fffff))) * (1 | (a >> 31));

}//will crash if the raw exponent == 0xff; reason for crash deviates from float standard; though a crash is warented.

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, basicly, it gets the mantissa, sets the sign on the mantisa,
           if(diff >= -1 || diff <= 1)//shifts it depending on exponent, finaly 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>

in every script when you can use the method above?

Your scripts seem to either give wrong answers, match mine or, in your case, they just crash...

Kira Komarov 06:12, 23 May 2012 (PDT)

FloatCompare is designed to tell you if two floats are close together. Closeness is not specified by some hardcoded constant but by the magnitude of the numbers. To do this we get the memory representation of the floats (as integers, it's what fui does). Then compare the mantissas and exponents. The integer parameter specifies the max difference between them.
As to magic numbers. Some are shared with Float2Hex and that has pretty good documentation. GTG -- Strife (talk|contribs) 11:50, 23 May 2012 (PDT)
Right. So where is "the magnitude of numbers" specified? To be more precise, if "closeness" is not specified by some hardcoded constant then how is it specified? Telling me what it is not specified by does not help because I can tell you what it is not specified by as well... For example, I am quite sure it's not specified by lederhosen... As it stands, aside from the crashes, it does not seem to do the job. Also, if you use integers to specify (this word has been abused so much, it should go on strike) the maximum difference (is this the "closeness" you mentioned?) between floats, then what is the point of it? I mean is there some mathematics involved or is it sort of an empirical measurement where some comparisons fail, some pass and some others crash?
I may take a look at Float2Hex later since that's another mystery but you suggested these functions for a project of mine (when I tried to explain determinism to you) and I guess it was a good choice not to use them. For example, you could take my 9-liner code and introduce another float which can give you the distance between those floats precisely (through elementary inequalities). I am not sure I follow your reasoning. In fact is there any reasoning at all? Are there any mathematical definitions that would explain what "closeness" means in your case?
Your girlfriend here tosses your functions to Linden as an example. However your functions fail the most basic tests (again, leaving aside the funny crashes) and are not explained at all. How do you expect Linden to take action on something that neither you, or her seem to fully comprehend let alone explain in detail?
She even cites your code with "//-- formating functions, please ignore", but how can you ignore that if it doesn't work properly. I don't even have to follow your obsessions on bit twiddling, I can just run the tests above and get undefined behavior (and that's putting it boldly because you just dump the function with no explanation, so I guess I don't know what the defined behavior is in the first place to be able to claim that it's undefined). Did you take this code from somewhere? Perhaps it had a point there...
Kira Komarov
Sorry didn't mean to get you riled with a short incomplete reply, I just ran out of time and didn't want to give you nothing. -- Strife (talk|contribs) 11:47, 24 May 2012 (PDT)
I wrote the function in the course of examining floating point arithmetic drift. That is to say the steady corruption of the lowest bits due to arithmetic operations. The problem is corruption manifests itself in the values of the bits in the mantissa of the float, it can even result in the exponent being increased.
I feel I need to explain how floats work so lets take a moment to go over that. However it's easier for me to provide code that replicates how it works than to put how it works into words.
A float is made of three parts. This is how it is stored in memory. All operators take it in this for, perform their operation and return a value in this form. A float is just a collection of bits that have meaning given a specific context.
  1. sign - 1 bit
  2. exponent - 8 bits (unsigned)
  3. mantissa - 23 bits (unsigned)
The value of a float is calculated as follows:

<lsl>float value(integer sign, integer exponent, integer mantissa){

   //make the sign 1 or -1, zero maps to 1
   sign = 1 | -!!sign;
   
   //For the sake of simplicity we will ignore NaN and Infinity values.
   
   //normalized range
   if(exponent)
        return sign * (0x800000 + mantissa) * llPow(2.0, exponent - 150);
   //denormalized range
   return sign * mantissa * llPow(2.0, -149);

}</lsl>

Now LSL is not C, in C you can take a float, do some hand waving and presto you can read the float value as if it were an integer value. That is to say, given the memory location of the float, read that location as if it were an integer. Since LSL does not let you do this (though there is nothing stopping you from doing this in LSO), I wrote a set of functions to approximate this. This is what FUI and IUF do (their only flaw is not properly handling nan and infinity). I wrote these functions of an LSL based LSO interpreter that I never finished. They are an evolutionary dead end a solution without a problem to solve.
The problem is that as you perform certain arithmetic operations you get drift. The lowest bits of the mantissa do not match up with the math. That is to say, the way we as humans evaluate the operations is slightly different than the way the computer does. Take for example (1.0 / 10) * 10. As humans we would say that it is equal to 1. The computer on the other hand will say it's not equal to 1. This is because the result of (1.0 / 10) is an approximation, the precise value would a different way of storing the value.
What this function does is let you compare the mantissa of the two floats. In it's most basic form it's ((mantissa_a - mantissa_b) > c) (this simplification doesn't take into consideration that the exponents won't match, or that the sign of the return is based on the larger value).