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

From Second Life Wiki
Jump to navigation Jump to search
m (→‎Try these: Minor nitpick)
 
(90 intermediate revisions by 6 users not shown)
Line 1: Line 1:
== What's the purpose of llFloatCompare? ==
== Ever considered making a fui16? ==
 
I was just looking around for how I could union a float into a 16 bit integer, perhaps using the [http://en.wikipedia.org/wiki/Half-precision_floating-point_format IEEE 754-2008 half-precision] format.  Yes, I know there would be losses, and I also know that SL olny has 32 bit integers.  The final goal for my application is to pack two floats into a single integer, via bitshifting the first into the high range and then ORing with the second, a la:
<source lang="lsl2">
// To pack:
float a = PI;
float b = 4.2;
integer result = (fui16(a) << 0x10) | fui16(b);
 
// To unpack:
float c = i16uf(result >> 0x10); // Might need to perform some further magic here to account for the fact that the sign bit doesn't shift.
float d = i16uf(result & 0xFFFF);
</source>
Of course, in the general case one can't make assumptions about range and resolution.  That said, in my application I ''can'' make such assumptions, and I'm just going to hack together something that works based on those assumptions.  I'm just here feeding the fetish that resulted in these very-useful-to-me functions (fui, iuf) in the first place. >:D
 
{{Unsigned|Cron Stardust}}
 
:Lets see, I could either work on [[Template:LSL Function]] et al as to split the notes section in half, or I could write fui16 a most clever function. Hmmm. Fun clever coding or Template drudge. Half precision doesn't have the same range, grrrr... -- '''[[User:Strife_Onizuka|Strife]]''' <sup><small>([[User talk:Strife_Onizuka|talk]]|[[Special:Contributions/Strife_Onizuka|contribs]])</small></sup> 20:57, 24 December 2012 (PST)
 
:Upon second though, I really don't want to dig into floating point math at the moment. Mostly you just need to add clipping to fui, change the constants and add infinity support to iuf. With clipping implemented the error correcting code can be removed. Here is my first crack at updating the constants. I've not tested it. -- '''[[User:Strife_Onizuka|Strife]]''' <sup><small>([[User talk:Strife_Onizuka|talk]]|[[Special:Contributions/Strife_Onizuka|contribs]])</small></sup> 21:35, 24 December 2012 (PST)
 
<source lang="lsl2">
integer fui16(float a)//Mono Safe, LSO Safe, Doubles Unsupported, LSLEditor Unsafe
{//union half-precision float to short integer
    if((a)){//is it non 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 > 65504.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 *= (0.0000152587890625 * (0x40000000 >> c))))) << 10 ) | b);
    }//for grins, detect the sign on zero. it's not pretty but it works. the previous requires a lot of unwinding to understand it.
    return (integer)((string)a == (string)(-0.0)) << 15;
}
 
float i16uf(integer a)
{//union short integer to half-precision float
    if((a & 0x7C00) == 0x7C00)
        return (1 | ((a << 16) >> 31)) * (float)"Infinity";
    return 0.000000059604644775390625 * (1 << (a - !!a)) * ((!!(a = (0x1f & (a >> 10))) << 10) | ((a & 0x3ff))) * (1 | ((a << 16) >> 31));
}
</source>
 
<source lang="lsl2">
// To pack:
float a = PI;
float b = 4.2;
integer result = (fui16(a) << 16) | fui16(b);
// To unpack:
float c = i16uf(result >> 16); // No magic needed, sign bit is handled correctly.
float d = i16uf(result);
</source>
::Just tested in SL with the following results: Had to place a closing paren in the first return statement of iuf16, I placed it just before the mult as I suspect that's what was intended. I wrote a simple ownersay of the existing test code which gave the following as the output: [[User:Cron Stardust|Cron Stardust]] 16:12, 25 December 2012 (PST)
<source lang="lsl2">
  a = 3.141593 = 0xC90FDBp-22
= c = 3.140625 = 0xC9p-6
  b = 4.200000 = 0x433333p-20
= d = 4.199219 = 0x433p-8</source>
::Further testing, looking for error cases, resulted in a lot of good: the functions seem to operate as they should.  I tested using all the constants demonstrated on the Wikipedia article and only one failed to match perfectly: 0.0000000596046 doesn't encode to 0x0001, instead it results in 0x0000. All other values seem to work great. [[User:Cron Stardust|Cron Stardust]] 22:00, 25 December 2012 (PST)
:: Also tested that with <code>a = -2.0</code> and <code>b = -2.0</code> that everything works out: no issues happen with the sign bit status that I could detect - so I took the liberty of changing the comment in your code. [[User:Cron Stardust|Cron Stardust]] 22:34, 25 December 2012 (PST)
 
:::I'm willing to bet it's a lack of precision in their approximation. Try: 0.000000059604644775390625 -- '''[[User:Strife_Onizuka|Strife]]''' <sup><small>([[User talk:Strife_Onizuka|talk]]|[[Special:Contributions/Strife_Onizuka|contribs]])</small></sup> 00:36, 26 December 2012 (PST)
 
:::I'm not 100% sure of the "<code>llFloor((llLog(a) / 0.6931471...)</code>" line but I think in this range it's accurate and we don't need to do any correction. The only way to know for sure is to try every value in the range [0.0001220703125, 65504.0]. I know it's not going to overflow so I've removed the overflow protection code. If you want to do that testing, here is the return line you would use which assumes that "c" is accurate: <code>return (0x3FF & (integer)((a / (float)("0x.004p"+(string)c)))) | (((c + 15) << 10 ) | b);</code> It has the added bonus of making fui16 LSLEditor safe. This was also a good demonstration of how the layers of correction code were bolted on. -- '''[[User:Strife_Onizuka|Strife]]''' <sup><small>([[User talk:Strife_Onizuka|talk]]|[[Special:Contributions/Strife_Onizuka|contribs]])</small></sup> 00:50, 26 December 2012 (PST)
 
This is a better version of what I was talking about but it needs extensive testing. If c is not accurate this will not work.
<source lang="lsl2">
integer c = llFloor((llLog(a) / 0.69314718055994530941723212145818)) + 15;//extremes will error towards extremes. No corrections are attempted.
return (0x3FF & (integer)((a * 0.03125 * (0x40000000 >> c)))) | ((c << 10) | b);
</source>
I've gone back and removed my string-float pow code since it can all be done with integers and float math. Faster this way. Less stressful for the sim.
-- '''[[User:Strife_Onizuka|Strife]]''' <sup><small>([[User talk:Strife_Onizuka|talk]]|[[Special:Contributions/Strife_Onizuka|contribs]])</small></sup> 01:32, 26 December 2012 (PST)
=== Testing ===
Just run the following code and it will tell you if c is accurate for the entire half-precision range (this is one of the things iuf is really good for, iterating over every float value). -- '''[[User:Strife_Onizuka|Strife]]''' <sup><small>([[User talk:Strife_Onizuka|talk]]|[[Special:Contributions/Strife_Onizuka|contribs]])</small></sup> 02:55, 26 December 2012 (PST)
<source lang="lsl2">
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 stride 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 a lot of unwinding to understand it.
    return ((string)a == (string)(-0.0)) << 31;
}


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:
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 warranted.


<lsl>
integer test(float a)//Mono Safe, LSO Safe, Doubles Unsupported, LSLEditor Unsafe
        float f_1 = 0.00000003;
{//union half-precision float to short integer
        float f_2 = 0.00000002;
    if((a))
         if(llFabs(f_1) - llFabs(f_2) > 0 ) {
    {//is it non zero?
             llOwnerSay("f_1 > f_2");
         if((a = llFabs(a)) < 0.0001220703125)//Denormalized range check & last stride of normalized range
             return;
             return FALSE;//the math overlaps; saves cpu time.
         }
        if(a > 65504.0)//Round up to infinity
        if(llFabs(f_1 - f_2) == 0.0) {
             return FALSE;//Positive or negative infinity
            llOwnerSay("f_1 = f_2");
         integer c = llFloor((llLog(a) / 0.69314718055994530941723212145818)) + 14;//extremes will error towards extremes. following yuch corrects it.
            return;
        return (integer)(a *= (0.0000152587890625 * (0x40000000 >> c))) - 1;
        }
    }//for grins, detect the sign on zero. it's not pretty but it works. the previous requires a lot of unwinding to understand it.
        llOwnerSay("f_1 < f_2");
    return FALSE;
</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:
float i16uf(integer a)
{//union short integer to half-precision float
    if((a & 0x7C00) == 0x7C00)
        return (1 | ((a << 16) >> 31)) * (float)"Infinity";
    return 0.000000059604644775390625 * (1 << (a - !!a)) * ((!!(a = (0x1f & (a >> 10))) << 10) | ((a & 0x3ff))) * (1 | ((a << 16) >> 31));
}


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


default {
default
     state_entry() {
{
         float f_1 = 0.999993;
     state_entry()
         float f_2 = 0.999992;
    {
         if(llFabs(f_1) - llFabs(f_2) > 0 ) {
         llOwnerSay("beginning test");llResetTime();
             llOwnerSay("Kira: f_1 > f_2");
       
            jump strife;
        integer badCount = 0;
        }
        integer badStart;
         if(llFabs(f_1 - f_2) == 0.0) {
       
             llOwnerSay("Kira: f_1 = f_2");
        integer start = fui(i16uf(1) / 2.0);//start just before min value, these will get clipped to zero.
             jump strife;
         integer end = fui(65536.0);//stop just after infinity
        integer i;
         for(i = start;i < end; i++)
            if(test(iuf(i)))
            {
                llSetText("bad:  "+(string)i, <1.0, 0.0, 0.0>, 1.0);
                if (badCount++ <= 0)
                {
                    badStart = i;
                    llOwnerSay("Found bad number: " + (string) i);
                }
            }
            else
             {
                llSetText("good: "+(string)i, <0.0, 1.0, 0.0>, 1.0);
                if (badCount > 0)
                {
                    llOwnerSay("Total of " + (string) badCount + " numbers, ending with " + (string) (i-1));
                    gBadBlockCounts = [badStart, i - 1] + gBadBlockCounts;
                    badCount = 0;
                }
            }
         llSetText("test complete.\ntook " + (string) llGetTime() + " secs over " + (string) (end - start) + " values\nsaw " + (string) llGetListLength(gBadBlockCounts) + " bad blocks.\nTouch for list of bad.", <0.0, 1.0, 1.0>, 1.0);
        state printout;
    }
   
}
 
state printout
{
    touch_end(integer index)
    {
        while (--index >= 0)
        {
            if (llGetListLength(gBadBlockCounts))
             {
                llRegionSayTo(llDetectedKey(index), 0,
                    "Bad blocks strided list, using stride pattern (block_start, block_end):"
                );
                llRegionSayTo(llDetectedKey(index), 0,
                    llDumpList2String(gBadBlockCounts, ", ")
                );
             }
            else
            {
                llRegionSayTo(llDetectedKey(index), 0,
                    "Nothing to report."
                );
            }
         }
         }
        llOwnerSay("Kira: f_1 < f_2");
@strife;
        llOwnerSay("Strife: " + (string)FloatCompare(f_1, f_2, 1));
     }
     }
}
}
</lsl>
</source>
::Tested this morning.  Went bad at values 864026624 through 900508790.  Not sure if there were further values: I set it up to run and then wen to work for the day, when I came back it had been yelling for a very long time... :P  Will see what I can do, however while I can understand the code, I've not yet been able to get my mind in deep enough to understand the algorithm. [[User:Cron Stardust|Cron Stardust]] 18:55, 26 December 2012 (PST)  EDIT: Just noticed that there was a function named test in there that wasn't used: I'd pasted in the fui16 function from above to make the code compile and run.  That may have invalidated the test, so I've going to retest usung the "test" function in place of the fui16 call in state_entry.  I've edited the above state_entry to reflect my current code.
::Just passed the 864026624 mark without errors, so yes: my previous test was invalid.  Continuing testing - just like GLaDOS likes. [[User:Cron Stardust|Cron Stardust]] 20:59, 26 December 2012 (PST)


----
:::I should have thought of this sooner, we don't need to test every value, just the ones who's values surround where the value of "c" changes. We only need to test ummm 84 values... just the powers of two and the immediate lesser values. -- '''[[User:Strife_Onizuka|Strife]]''' <sup><small>([[User talk:Strife_Onizuka|talk]]|[[Special:Contributions/Strife_Onizuka|contribs]])</small></sup> 01:44, 27 December 2012 (PST)


'''Test 1, Your method fails.'''
<source lang="lsl2">
integer fui(float a)//Mono Safe, LSO Safe, Doubles Unsupported, LSLEditor Unsafe
{//union float to integer
    if((a))
    {//is it non zero?
        integer b = (integer)(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 stride 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.
        a /= (float)("0x1p"+(string)(c -= ((c >> 31) | 1)));
        integer bb = ((integer)a - (3 <= a));
        return (0x7FFFFF & (integer)(a * (0x1000000 >> bb))) | (((c + 126 + bb) << 23 ) | b);
    }//for grins, detect the sign on zero. it's not pretty but it works. the previous requires a lot of unwinding to understand it.
    return (integer)((string)a == (string)(-0.0)) << 31;
}


For
float iuf(integer a)
<lsl>
{//union integer to float
float f_1 = 0.9999993;
    integer aa = 0xff & (a >> 23);
float f_2 = 0.9999992;
    return ((float)("0x1p"+(string)((aa | !aa) - 150))) * ((!!aa << 23) | ((a & 0x7fffff))) * (1 | (a >> 31));
</lsl>
}//will crash if the raw exponent == 0xff; reason for crash deviates from float standard; though a crash is warranted.
 
integer get_log2(float a)
{//we use the natural log because testing showed it's more accurate than log10.
    return llFloor((llLog(a) / 0.69314718055994530941723212145818));//must be divide, multiplying the reciprocal adds extra corruption.
}
 
integer test_log2(integer power)
{
    float a = (float)("0x1p"+(string)power);
    float b = iuf(fui(a) - 1);//The underflow is in our favor and does what we want.
    integer c = get_log2(a);
    integer d = get_log2(b);
    integer e = (c == power) && ((d + 1) == power);
//   llOwnerSay(llList2CSV([power, c, d, Float2Hex(a), Float2Hex(b), int2hexdword(fui(a)), int2hexdword(fui(b)), e]));
    return e;
}


'''Output'''
default
{
    state_entry()
    {
        integer start = -25;
        integer end = 17;
        integer i;
        for(i = start; i <= end; i++)
            if(!test_log2(i))
                llOwnerSay((string)i);
    }
}
</source>
:: Ok, so executed - though I'm leaving the other script running, and it seems good thus far; will report when it completes.  The results from this new test are that I saw values reported in the following ranges: [-25, -16], -14, [-12, -2], [3, 17] -- [[User:Cron Stardust|Cron Stardust]] 19:14, 27 December 2012 (PST)
 
::: Well that is bad. Guess c is not accurate so we can't remove the correction code (the overflow code can still be removed). I wonder what happens if you feed it NaN. Probably need a special case just for that. -- '''[[User:Strife_Onizuka|Strife]]''' <sup><small>([[User talk:Strife_Onizuka|talk]]|[[Special:Contributions/Strife_Onizuka|contribs]])</small></sup> 20:45, 27 December 2012 (PST)
 
::: I remembered how the correction code works and I fused one of the moving parts. I don't think it's necessary for this range but I'm adding it back in. Since "c" could be off by one in either direction, this gives us three possible outcomes when generating new-a.
:::# It's too small, the result is that new-a is in range [0.5, 1)
:::# C is correct, new-a is in range [1, 2)
:::# It's too big, the result is that new-a is in range [2,4)
:::Solution? Map these three outcomes to three consecutive values so that they can be used to adjust c. Truncating new-a gives us [0.5, 1)->0, [1, 2)->1, [2,4)->{2,3}. Yes they are consecutive but we need to map the final set to a single value. Hence the subtract by (3 <= a).
:::It's a shame I never documented which edge cases this code fixed. It would take a lot of the guess work out. -- '''[[User:Strife_Onizuka|Strife]]''' <sup><small>([[User talk:Strife_Onizuka|talk]]|[[Special:Contributions/Strife_Onizuka|contribs]])</small></sup> 21:22, 27 December 2012 (PST)
: For what it's worth, the old test code finally completed!  Took about 6-8 days continuous* calculation in my home region. (* script reported 521479.8 seconds, but I'd had to stop and fix a UI error after several hours of computation, and picked up where I'd left off.)  Here's the report: [[User:Cron Stardust|Cron Stardust]] 18:14, 5 January 2013 (PST)
<pre>
<pre>
Object: Kira: f_1 > f_2
Bad blocks list, using stride pattern [block_start, block_end]:
Object: Strife: 0
[
[1191182336, 1191182336], [1182793721, 1182793727], [1174405120, 1174405120], [1166016505, 1166016511],
[1157627901, 1157627903], [1149239293, 1149239295], [1140850685, 1140850687], [1132462077, 1132462079],
[1124073469, 1124073471], [1115684861, 1115684863], [1107296255, 1107296255], [1098907647, 1098907647],
[1090519039, 1090519039], [1048575999, 1048575999], [1040187390, 1040187391], [1031798782, 1031798783],
[1023410174, 1023410175], [1015021564, 1015021567], [1006632956, 1006632959], [ 998244348,  998244351],
[ 989855740,  989855743], [ 981467132,  981467135], [ 973078524,  973078527], [ 964689912,  964689919]
]
</pre>
</pre>


They are not equal... What is this?
== fui support for NaN and Inf ==
 
I must be one of the few people who have found an actual use to fui :) I'm using it for compact lossless transmission of floats over HTTP to a PHP server in base 64 (after removing the == and the trailing A's), and I decode it in the server using unpack() and some string manipulation. I would love to see fui support NaN, Inf and -Inf, but more for the sake of completion than because I have a real use (except possibly debugging). Maybe it's as straightforward as adding this on top?
<source lang="lsl2">
  if ((string)a == "Infinity") return 0x7F800000;
  if ((string)a == "-Infinity") return 0xFF800000;
  if ((string)a == "NaN") return 0x7FC00000; // to choose one
</source>
I guess there's no way to extract the actual bits of a NaN, or is there? --[[User:Pedro Oval|Pedro Oval]] 05:09, 26 December 2012 (PST)
 
Oops, I've corrected "Inf" to read "Infinity" above. --[[User:Pedro Oval|Pedro Oval]] 07:58, 26 December 2012 (PST)
 
:Sure, I don't see why not. I'll think about ways of doing it (I'm not sure if successive tests is really the best way of doing so). The reason Infinity was never handled was that LSO doesn't allow it. These functions were originally conceived before Mono was on the horizon (at the time I had wanted to build an LSO bytecode emulator in LSL). -- '''[[User:Strife_Onizuka|Strife]]''' <sup><small>([[User talk:Strife_Onizuka|talk]]|[[Special:Contributions/Strife_Onizuka|contribs]])</small></sup> 01:01, 27 December 2012 (PST)
:It's done. -- '''[[User:Strife_Onizuka|Strife]]''' <sup><small>([[User talk:Strife_Onizuka|talk]]|[[Special:Contributions/Strife_Onizuka|contribs]])</small></sup> 22:43, 27 December 2012 (PST)


----
::You will be pleased (or horrified) to see that [[Float2Hex]] and [[Float2Sci]] both support NaN and Infinities now. -- '''[[User:Strife_Onizuka|Strife]]''' <sup><small>([[User talk:Strife_Onizuka|talk]]|[[Special:Contributions/Strife_Onizuka|contribs]])</small></sup> 00:22, 29 December 2012 (PST)


'''Test 2, Your method fails.'''
== Alternate code ==


For:
I was pondering alternate ways to <code>b >> (13 + ((b & 0x3FFF) == 0x2000))</code> because it's just so ugly.
<lsl>
*<code>b >> (((0x3FFF & b ^ 0x1FFF) + 0x34001) >> 14)</code> - we flip the low bits, so if all of them are on, that is to say our condition has been met, adding one will cause a carry into the 14th bit.
float f_1 = 0.9999003;
*<code>b >> ((0x38000 - (0x3FFF & b ^ 0x2000)) >> 14)</code> - similar to above, we turn the high bit off, so if it's zero it doesn't cause a carry from above.
float f_2 = 0.9999002;
*<code>b / ((((0x3FFF & b ^ 0x1FFF) + 0x4001) & 0xC000) >> 1)</code>
</lsl>
It's a shame there isn't a way to further improve it. It's an ugly mess that only gets uglier. ~_~ -- '''[[User:Strife_Onizuka|Strife]]''' <sup><small>([[User talk:Strife_Onizuka|talk]]|[[Special:Contributions/Strife_Onizuka|contribs]])</small></sup> 23:22, 28 December 2012 (PST)


'''Output'''
= Bugs in iuf and fui =
<pre>
== Bug in iuf ==
Object: Kira: f_1 > f_2
 
Object: Strife: 0
iuf does not support exponent parts of 2<sup>-104</sup> or below. The reason is that in an intermediate step, it is trying to calculate 0x1p-127 or below, which yields 0. For exaple, iuf(0x0bffffff) returns 0, while iuf(0x0c00000) returns the correct result.
</pre>


Still equal? WTH?
Rather than trying to find a fix, I created my own iuf that seems to work, even if it's unoptimized and not very fast (though it contains a small trick to reduce the total number of iterations to a maximum of 25 or so):


----
<source lang="lsl2">
float iuf(integer e)
{
    float result = e & 0x7FFFFF;
    integer sign = e & 0x80000000;
    e = ((e >> 23) & 0xFF) + -150;
    // deal with Inf/NaN
    if (e == 105) { if (result) return (float)"NaN"; if (sign) return (float)"-Inf"; return (float)"Inf"; }
    if (e ^ -150)
    {
        result += 0x800000;
        while (e > 9) { result *= 512; e += -9; }
        while (e < -9) { result *= 0.001953125; e += 9; }
        while (e > 0) { result *= 2; --e; }
        while (e < 0) { result *= 0.5; ++e; }
        if (sign) return -result;
        return result;
    }
    if (sign) return -(result*1e-45);
    return result*1e-45;
}
</source>


'''Test 3, We both succeed.'''
A fix might be along the lines of not applying the -23 offset in advance (as usage of -150 implies) but postmultiplying by 2^-23 instead. I haven't tried though. --[[User:Pedro Oval|Pedro Oval]] 09:37, 8 February 2013 (PST)


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


'''Output'''
:If the denormalized range isn't working then Float2Hex is likely broken too. hmmm -- '''[[User:Strife_Onizuka|Strife]]''' <sup><small>([[User talk:Strife_Onizuka|talk]]|[[Special:Contributions/Strife_Onizuka|contribs]])</small></sup> 11:11, 9 February 2013 (PST)
<pre>
Object: Kira: f_1 > f_2
Object: Strife: 1
</pre>


----
::It's not just the denormal range, it affects the normal range too. 2<sup>-104</sup> is small but not denormal; the denormal range starts at 2<sup>-127</sup>. --[[User:Pedro Oval|Pedro Oval]] 16:22, 9 February 2013 (PST)


'''Test 4, We both fail.'''
:::I start the denormalized range a power of two early but if it's failing all the way up to -104... that's a big problem. I'll have to play with it. Multiplying by a power of two shouldn't trash the mantissa. -- '''[[User:Strife_Onizuka|Strife]]''' <sup><small>([[User talk:Strife_Onizuka|talk]]|[[Special:Contributions/Strife_Onizuka|contribs]])</small></sup> 20:29, 11 February 2013 (PST)


<lsl>
::::Maybe I misunderstood. If what you meant is that hex floating point format is unable to produce denormal numbers, then that's exactly the problem, yes. 0x1p-127 produces 0, even if the denormal range can represent numbers down to 0x1p-149. What I meant is that inputs to the function representing exponents of -104 or below failed (as these involved calculation of 0x1p-127 or below). Now some good news. The new function revised to use [[llPow]] works correctly for small exponents and seems to work correctly for the rest. Also, under Mono at least its performance is about 2.5 times that of the old (string operations are s-l-o-w). --[[User:Pedro Oval|Pedro Oval]] 06:24, 12 February 2013 (PST)
float f_1 = 0.000000000000000000000000000000000000000000002;
float f_2 = 0.000000000000000000000000000000000000000000001;
</lsl>


'''Output'''
== Bug in fui ==
<pre>
Object: Kira: f_1 = f_2
Object: Strife: 0
</pre>


----
Alas, iuf is not the only one with problems. For exponents of -2 or less, and mantissas in the vicinity of 0x7FFFFF, results are almost always wrong. Examples:


Here are a few funny ones:
* <code>fui(iuf(0x017fffdd)) = 0x01ffffee</code> (the first one failing with mantissa > 0x7FFF00, exponent is -124)
* <code>fui(iuf(0x017fffff)) = 0x01ffffff</code> (the last one failing with that exponent; all in the middle also fail)
* <code>fui(iuf(0x01ffffc5)) = 0x027fffe2</code> (next one failing)
* <code>fui(iuf(0x01ffffff)) = 0x027fffff</code> (last one with that exponent, all in the middle fail)
* <code>fui(iuf(0x037ffffd)) = 0x03fffffe</code> (for this exponent the failure range is shorter - just 3 numbers)
* <code>fui(iuf(0x38ffffff)) = 0x38ffffff</code> (for a few exponents there are no failures at all; this is an example)
* <code>fui(iuf(0x3e7fffff)) = 0x3effffff</code> (last one failing)


'''Test 5, My method works, your method just crashes with Math Error'''
I've manually checked some of the iuf conversions and they worked correctly with the code I posted above, so the problem is in fui. For example: <code>if (iuf(0x25fffff6) == .44408894e-15) llOwnerSay("iuf ok");</code> says "iuf ok", but fui(.44408894e-15) gives 0x267ffffb, not 0x25fffff6. --[[User:Pedro Oval|Pedro Oval]] 11:48, 8 February 2013 (PST)


<lsl>
:Are you doing these tests in Mono, LSO or both? -- '''[[User:Strife_Onizuka|Strife]]''' <sup><small>([[User talk:Strife_Onizuka|talk]]|[[Special:Contributions/Strife_Onizuka|contribs]])</small></sup> 10:48, 9 February 2013 (PST)
float f_1 = 3.402E+38;
float f_2 = 3.403E+38;
</lsl>


'''Output'''
::I tested the full range only in Mono, but a quick run seems to show that LSO is affected in exactly the same way. At least the range starts in the same number (0x017fffdd) and produces the same result (0x01ffffee). --[[User:Pedro Oval|Pedro Oval]] 16:31, 9 February 2013 (PST)
<pre>
Object: Kira: f_1 < f_2
Object: Object [script:New Script] Script run-time error
Object: Math Error
</pre>


----
:It will take me some time (maybe a few weeks) to get around to this, I'm very busy currently. -- '''[[User:Strife_Onizuka|Strife]]''' <sup><small>([[User talk:Strife_Onizuka|talk]]|[[Special:Contributions/Strife_Onizuka|contribs]])</small></sup> 11:14, 9 February 2013 (PST)


This one too:
::I admit to kicking off Pedro's investigation, as I tried to use iuf and fui in Mono and found that fui (iuf (x) )  gave zero for all values of x from 1 to 3 million, at which point when I gave up :) They seem to work a lot better in LSO from my briefer tests so far. [[User:Omei Qunhua|Omei Qunhua]] 15:52, 9 February 2013 (PST)


'''Test 6, My method works, your method just crashes with Math Error'''
= Try these =
I've swapped out the hack pow method for the real function. I'm hoping this will fix iuf.


<lsl>
I've also expanded the compressed lines so that they are easier to tinker with. I've enlarged the supported range of <code>a</code> in the error calculation, the range supported has changed from [0.5, 4) to [0.25, 4).
float f_1 = 3.403E+38;
float f_2 = 3.402E+38;
</lsl>


'''Output'''
<source lang="lsl2">
<pre>
integer fui(float a)//Mono Safe, LSO Safe, Doubles Unsupported, LSLEditor Unsafe
Object: Kira: f_1 > f_2
{//union float to integer
Object: Object [script:New Script] Script run-time error
    if((a)){//is it greater than or less than zero?
Object: Math Error
        integer b = (a < 0) << 31;//the sign, but later this variable is reused to store the shift
</pre>
        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
        integer c = llFloor((llLog(a) / 0.69314718055994530941723212145818));//extremes will error towards extremes. following yuch corrects it.
        c -= (c >> 31) | 1;//this sets up an error towards the zero
        a /= llPow(2.0, c);//a should now be in the range [0.25, 4).
        integer d = (integer)a + (0.5 >= a) - (3 <= a);//This calculates the actual error. It does so by mapping the value of a to the error.
        //Specifically we take advantage of the where the normalization bit has been shifted.
        //[0.25, 0.5) == -2, [0.5, 1) == -1, [1,2) == 0, [2, 4) == 1 ~ note the actual values of d are off by two.
        llOwnerSay(llList2CSV([0x800000 == (0xF800000 & (integer)(a * (0x2000000 >> d))), c, d, a, (a < 0.25) || (a > 4)]));//first value tells you if you got d right or wrong.
        //The check works by seeing where the normalization bit has been shifted. The normalization bit gets chopped off with 0x7FFFFF.
        return (0x7FFFFF & (integer)(a * (0x2000000 >> d))) | (((c + 125 + d) << 23 ) | 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)) << 31;//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 0x7FFFFFFF;//NaN time! We have no way to tell NaN's apart so lets just choose one.
}


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...
float iuf(integer a)
{//union integer to float
    if((a & 0x7F800000) == 0x7F800000)
        return (1 | (a >> 31)) * (float)llList2String(["NaN","Infinity"], !(a & 0x7FFFFF));
    integer b = 0xff & (a >> 23);
    return llPow(2.0, (b | !b) - 150) * (((!!b) << 23) | ((a & 0x7fffff))) * (1 | (a >> 31));
}//will crash if the raw exponent == 0xff; reason for crash deviates from float standard; though a crash is warranted.
</source>
::The above fails at every number from the range I tested (mantissa in the vicinity of 0x3FFFFF, exponent -2 and below). --[[User:Pedro Oval|Pedro Oval]] 06:24, 12 February 2013 (PST)
:::More specifically, it seems to fail for all numbers with bit 22 set (mask 0x00400000). --[[User:Pedro Oval|Pedro Oval]] 16:53, 13 February 2013 (PST)


* Could you please clarify what they are supposed to do?
This uses a modified error correction code. I'm not sure if it will be better or worse. It has the potential to run out the high side of the stated bounds on <code>a</code> but I don't think this will be the case. We shall have to see.
* Why include this chunk of obfuscated garbage with magic numbers:


<lsl>
<source lang="lsl2">
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 non 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) << 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
         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
            return b | 0x7F800000;//Positive or negative infinity
         integer c = llFloor((llLog(a) / 0.69314718055994530941723212145818));//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.
         return (0x7FFFFF & (integer)(a * (0x1000000 >> b))) | (((c + 126 + (b = ((integer)a - (3 <= (a /= (float)("0x1p"+(string)(c -= ((c >> 31) | 1)))))))) << 23 ) | b);
        --c;//Make it error down. Hopefully this won't shift anyone into the [4, 8) range.
     }//for grins, detect the sign on zero. it's not pretty but it works. the previous requires alot of unwinding to understand it.
        a /= llPow(2.0, c);//a should now be in the range [0.5, 4), we assume no error is added.
     return ((string)a == (string)(-0.0)) << 31;
        integer d = (integer)a - (3 <= a);//This calculates the actual error. As it turns out, the integer portion of a is {0,1,2,3} the errors in c are {-1,0,1,1}
        llOwnerSay(llList2CSV([0x800000 == (0xF800000 & (integer)(a * (0x2000000 >> d))), c, d, a]));
         return (0x7FFFFF & (integer)(a * (0x1000000 >> d))) | (((c + 126 + d) << 23 ) | 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)) << 31;//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 0x7FFFFFFF;//NaN time! We have no way to tell NaN's apart so lets just choose one.
}
float iuf(integer a)
{//union integer to float
    if((a & 0x7F800000) == 0x7F800000)
        return (1 | (a >> 31)) * (float)llList2String(["NaN","Infinity"], !(a & 0x7FFFFF));
    integer b = 0xff & (a >> 23);
    return llPow(2.0, (b | !b) - 150) * (((!!b) << 23) | ((a & 0x7fffff))) * (1 | (a >> 31));
}//will crash if the raw exponent == 0xff; reason for crash deviates from float standard; though a crash is warranted.
</source>
::The above passed the test in the range where version 1 failed (vicinity of 0x7fffff for mantissa and exponents up to -2). Also, fui(iuf(i))==i produces no mismatch in a random sample of 2M integers (version 1 produced 2 mismatches in a random sample of 1M). Looks promising. I'm not testing it in LSO though - too slow :) --[[User:Pedro Oval|Pedro Oval]] 06:24, 12 February 2013 (PST)
 
:::A distributed computation over the whole range of integers except those representing NaN and Inf (I used 64 scripts in 16 sims computing 64M numbers each) has showed that the fui and iuf functions above work flawlessly for every supported integer. Those representing NaN were not tested for the obvious reason of unavoidable data loss. We have a winner. Thank you so very very much. --[[User:Pedro Oval|Pedro Oval]] 21:34, 13 February 2013 (PST)
 
::::This is kind of scary, I wasn't expecting this version to work. The other version was the one I was expecting to work. Sounds like there were two bugs. I'll compress it back into an optimized form. This makes me worry about Float2Hex working, since it sounds like the string->float typecast doesn't like very small numbers. I wonder if there is a cap on the exponent? I shall have to test. -- '''[[User:Strife_Onizuka|Strife]]''' <sup><small>([[User talk:Strife_Onizuka|talk]]|[[Special:Contributions/Strife_Onizuka|contribs]])</small></sup> 11:08, 14 February 2013 (PST)
 
::::: I've made some experiments now. <code>(float)"something"</code> seems to fail under Mono whenever "something" results in a number with an absolute value less than <code>0x1p-126</code>, no matter if it's hex or otherwise.
 
::::: However, <code>(vector)"&lt;something,something,something>"</code> and <code>(rotation)"&lt;something,something,something,something>"</code> both work fine with numbers in that range even in Mono.
 
::::: Go figure... -- [[User:Pedro Oval|Pedro Oval]] ([[User talk:Pedro Oval|talk]]) 05:41, 19 September 2015 (PDT)
 
 
<source lang="lsl2">
integer fui(float a)//Mono Safe, LSO Safe, Doubles Unsupported, LSLEditor Unsafe
{//union float to integer
    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
        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
        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 /= llPow(2.0, c)))))) << 23 ) | 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)) << 31;//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 0x7FFFFFFF;//NaN time! We have no way to tell NaN's apart so lets just choose one.
}
}
   
   
float iuf(integer a)
float iuf(integer a)
{//union integer to float
{//union integer to float
     return ((float)("0x1p"+(string)((a | !a) - 150))) * ((!!(a = (0xff & (a >> 23))) << 23) | ((a & 0x7fffff))) * (1 | (a >> 31));
     if((a & 0x7F800000) == 0x7F800000)
}//will crash if the raw exponent == 0xff; reason for crash deviates from float standard; though a crash is warented.
        return (1 | (a >> 31)) * (float)llList2String(["NaN","Infinity"], !(a & 0x7FFFFF));
    return llPow(2.0, (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 warranted.
</source>
 
 
Lots to comment on, and MediaWiki's standard for follow-up responses is quite inconvenient for this purpose. I hope you don't mind if I post this reply without indentation.
 
First, I'm pretty sure that the failure of the previous function must be due to a bug somewhere, not to the algorithm, given that the failure pattern seems very clear and consistent. I haven't analyzed it so I can't tell more for now.
 
Second, your sentence "I wonder if there is a cap on the exponent?" suggests to me that you have missed my last reply in the [[#Bug in iuf|iuf bug discussion]].
 
Third, the "compressed" versions work, though I haven't tested the whole range, just a random sample of 10M integers.
 
Fourth, the NaN test is flawed. if((a)) is equivalent to if(!(a==0)) and is true for NaNs.
 
Fifth, the usual NaN returned by gcc is 0x7FC00000, so I suggest to use that for possible interoperability. It has the "quiet" bit set and nothing else.
 
Sixth, I see quite some room for memory optimizations in Mono for both functions, while minimally affecting performance if at all. Changes include:
 
* Changing division to multiplication where possible.
* Changing <= to < and >= to > where possible. a <= b is equivalent to !(a > b) both in size and in behavior even with NaNs (sad, I know).
* Changing subtraction to addition of opposite (a-b to a+-b).
* Changing -constant to its hex representation.
* Changing 2.0 to 2
* Changing << constant to * constant (>> and / take the same bytes so no gain there, plus there are sign issues).
* Changing (a & constant) == constant to !(~a & constant).
* Using localint^localint instead of 0.
* Change the logic of Infinity and NaN in iuf, to get rid of the list, using that (float)"Inf" yields infinity and that parsing stops at the first invalid character without invalidating the previous results.
* Changing (string)-0.0 to "-0.000000" (this one is the only one I don't have reliable experimental results to back, but I believe it will save memory; it almost surely will perform better by not needing to build that string at run time).
 
Here are Mono-memory-optimized versions taking into account all of these:


integer FloatCompare(float a, float b, integer c)
<source lang="lsl2">
{//compare floats and allow for a margin of error, requires fui().
integer fui(float a)//Mono Safe, ????LSO Safe???? (untested), Doubles Unsupported, LSLEditor Unsafe
     if(a - b)//(c) Strife Onizuka 2006
{//union float to integer
    {//they are not equal
     if(a){//is it nonzero?
         //First we convert the floats to integer form, as they would be in memory;
         integer b = (a < 0) * 0x80000000;//the sign, but later this variable is reused to store the shift
         integer a_i = fui(a);
         if((a = llFabs(a)) < 2.3509887016445750159374730744445e-38)//Denormalized range check & last stride of normalized range
        integer b_i = fui(b);
            return b | (integer)(a / 1.4012984643248170709237295832899e-45);//the math overlaps; saves cpu time.
        integer a_e = (a_i >> 23) & 0xff;
         if(a > 3.4028234663852885981170418348452e+38)//Round up to infinity
        integer b_e = (b_i >> 23) & 0xff;
            return b | 0x7F800000;//Positive or negative infinity
         if(!(a_e || b_e) || //to disable the +/- roll under support put a // just before the !
        if(a > 1e-45)//It should at this point, except if it's NaN
            ((a_i & 0x80000000) == (b_i & 0x80000000)))//sign match check
         {
         {//start by getting and testing the difference, this is what limits c
             integer c = ~-llFloor((llLog(a) / 0.69314718055994530941723212145818));//extremes will error towards extremes. following yuch corrects it.
             integer diff = a_e - b_e;//ugly is fast, basicly, it gets the mantissa, sets the sign on the mantisa,
            return (0x7FFFFF & (integer)(a * (0x1000000 >> b))) | (((c + 126 + (b = ((integer)a + -(3 <= (a *= llPow(2, -c)))))) * 0x800000 ) | b);
            if(diff >= -1 || diff <= 1)//shifts it depending on exponent, finaly executes the test.
         }//the previous requires a lot of unwinding to understand it.
                if(llAbs(((((a_i & 0x7FFFFF) | (!!a_e << 23)) * ((a_i >> 31) | 1)) >> !~-diff) -
         return 0x7FC00000;//NaN time! We have no way to tell NaN's apart so lets just choose one.
                        ((((b_i & 0x7FFFFF) | (!!b_e << 23)) * ((b_i >> 31) | 1)) >> !~diff)) <= c)
                    jump out;
         }
         return (a > b) - (a < b);
     }
     }
     @out;
     return ((string)a == "-0.000000") * 0x80000000;//for grins, detect the sign on zero. it's not pretty but it works.
     return 0;
}//Mono does not support indeterminates so I'm not going to worry about it.
 
float iuf(integer a)
{//union integer to float
    if(!(~a & 0x7F800000))
        return (float)llGetSubString("-nan-inf", 4 * !(a & 0x7FFFFF) + (a > (a^a)), 7);
     return llPow(2, (a | !a) + 0xffffff6a) * (((!!(a = (0xff & (a >> 23)))) * 0x800000) | ((a & 0x7fffff))) * (1 | (a >> 31));
}
}
</lsl>
</source>
Further possible optimizations include replacing other divisions with multiplications where possible (llLog(a) / 0.693... might be a candidate, but it's at the core of the algorithm so I didn't dare) and replacing <= with < in the (3 <= ...) if possible. If it's equivalent to (2 < ...) then the latter is preferable. Also, although I've not touched them, source code wise, the floats used have an unnecessary precision; using respectively 2.3509887e-38, 1e-45, 3.4028235e+38, and 0.6931472, the results are the same. You can check that doing: float f1 = 2.3509887016445750159374730744445e-38; float f2 = 2.3509887e-38; if (f1 == f2) llOwnerSay("yes"); outputs yes, and similarly for the rest. Doubles are not supported after all.


in every script when you can use the method above?
For the sake of verifiability, here's my simpler test program (not the distributed one, although it's along the same lines):
<source lang="lsl2">
string tohex(integer i)
{
    string hex = "0123456789ABCDEF";
    string result;
    integer iter = 9;
    while (--iter)
    {
        result = llGetSubString(hex, i & 0xF, i & 0xF) + result;
        i = i >> 4;
    }
    return result;
}


Your scripts seem to either give wrong answers, match mine or, in your case, they just crash...
default
{
    state_entry()
    {
        if (fui(iuf(0x7F800000)) ^ 0x7F800000) llOwnerSay("Fail at +inf");
        if (fui(iuf(0xFF800000)) ^ 0xFF800000) llOwnerSay("Fail at -inf");
        if (fui(iuf(0x7FC00000)) ^ 0x7FC00000) llOwnerSay("Fail at NaN1");
        if (fui(iuf(0xFFC00000)) ^ 0x7FC00000) llOwnerSay("Fail at NaN2");
        if (fui(iuf(0x7FC0FFFF)) ^ 0x7FC00000) llOwnerSay("Fail at NaN3");
        if (fui(iuf(0xFFC00001)) ^ 0x7FC00000) llOwnerSay("Fail at NaN4");
        if (fui(iuf(0x00000000)) ^ 0x00000000) llOwnerSay("Fail at 0.0");
        if (fui(iuf(0x80000000)) ^ 0x80000000) llOwnerSay("Fail at -0.0");
        if (fui(iuf(0x00000001)) ^ 0x00000001) llOwnerSay("Fail at 1e-45");
        if (fui(iuf(0x80000001)) ^ 0x80000001) llOwnerSay("Fail at -1e-45");
        if (fui(iuf(0xFF7FFFFF)) ^ 0xFF7FFFFF) llOwnerSay("Fail at ~ -1e38");
        integer a;
        integer iter;
        while (++iter)
        {
            integer rand = (integer)llFrand(65536) + 65536 * (integer)llFrand(65536);
            if (!(iter & 0xFFFF)) llSetText((string)iter, <1,1,1>, 1);


Kira Komarov 06:12, 23 May 2012 (PDT)
            if (fui(iuf(rand)) != rand) // we let it run for NaNs too to detect crashes
            {
                if (~rand & 0x7F800000)
                {
                    llSetText("Fail at rand = 0x" + tohex(rand) + ", fui(iuf(rand)) = 0x" + tohex(fui(iuf(rand))), <1,1,1>,1);
                    return;
                }
            }
        }
        llSetText("Done", <1,1,1>, 1);
    }
}
</source>
I simply let it run while desired.


: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.
--[[User:Pedro Oval|Pedro Oval]] 11:02, 15 February 2013 (PST)
:As to magic numbers. Some are shared with [[Float2Hex]] and that has pretty good documentation. GTG -- '''[[User:Strife_Onizuka|Strife]]''' <sup><small>([[User talk:Strife_Onizuka|talk]]|[[Special:Contributions/Strife_Onizuka|contribs]])</small></sup> 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 hadn't seen your previous comment, thank you for pointing it out. I'll try to patch Float2Hex so it doesn't use exponents less than -127 (it use to work on Mono ~_~).


::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?
:You are correct, the extra precision is unnecessary for single precision. I use these values because Float2Hex uses them, and I've written versions of Float2Hex that support higher precisions. I wrote both originally in ESL (LSL fed through a C Pre-processor) with extensive use of macros.


::Your [https://jira.secondlife.com/browse/SCR-119 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?
:I tend to optimize for LSO bytecode and stack size (I know it's insane but old habits die hard). Which is why I wasn't using llPow. I'm not surprised there is a bytecode savings between 2 and 2.0 for Mono but is it really faster? I would have though the compiler would have put in the implicit typecast there... unless LL got smart... hmmm. Anyway we are well beyond optimizing for LSO.


::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...
:You could try flipping the divide in the log2 calculation, I'm not sure if the error recovery is good enough. My recollection from my initial testing was that using a multiply instead of the divide widened the range of the errors.


::Kira Komarov
:I never wanted to committing to inlining "-0.000000" in fear they might change the typecast on me. We won that fight a few years ago so it should be ok.


:::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)
:Quick question, what does the following do? Trying to figure out how screwed I am. I am hoping to avoid having Float2Hex revert to base 10 for |x| < 0x1p-127 (I don't even know of a good way to do that x_x).
<source lang="lsl2">default {
    state_entry() {
        llOwnerSay((string)(0.0 != (float)"0x0.4p-127"));
    }
}</source>


:I know why fui was crashing. 3.403E+38; is getting rounded up to <code>inf</code>. When I wrote fui and iuf (3 or 4 years before Mono), infinity wasn't supported by LSL, its assumed that infinity and nan would crash the script, hence the comment at the end of iuf (the 0xff exponent is set aside for floating point magic numbers like nan and infinity).
:Unless testing proves otherwise, we don't want to swap in a multiplication for a shift, at the CPU level shift should be faster. Swapping a subtract out for an add-negation similarly won't give you much as negation would be an extra instruction (unless the negation is being done on a constant than it's no extra).


: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.
:1e-45 makes me uneasy. I know it works but it just looks wrong to me. I am cool with excessive precision.


: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.
:I've made a few changes. I couldn't help myself. I used the dreaded negative indexing on llGetSubString, ''with wraparound''. -- '''[[User:Strife_Onizuka|Strife]]''' <sup><small>([[User talk:Strife_Onizuka|talk]]|[[Special:Contributions/Strife_Onizuka|contribs]])</small></sup> 22:16, 16 February 2013 (PST)
: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.
<source lang="lsl2">
:#sign - 1 bit
integer fui(float a)//Mono Safe, LSO Safe, Doubles Unsupported, LSLEditor Unsafe
:#exponent - 8 bits (unsigned)
{//union float to integer
:#mantissa - 23 bits (unsigned)
    if(a){//is it nonzero?
:The value of a float is calculated as follows:
        integer b = (a < 0) * 0x80000000;//the sign
<lsl>float value(integer sign, integer exponent, integer mantissa){
        if((a = llFabs(a)) < 2.3509887016445750159374730744445e-38)//Denormalized range check & last stride of normalized range
    //make the sign 1 or -1, zero maps to 1
            return b | (integer)(a / 1.4012984643248170709237295832899e-45);//the math overlaps; saves cpu time.
    sign = 1 | -!!sign;
        if(a > 3.4028234663852885981170418348452e+38)//Round up to infinity
   
            return b | 0x7F800000;//Positive or negative infinity
    //For the sake of simplicity we will ignore NaN and Infinity values.
        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
    //normalized range
            return b | (0x7FFFFF & (integer)(a * (0x1000000 >> c))) | ((126 + (c = ((integer)a - (3 <= (a *= llPow(2, -c))))) + c) * 0x800000);
    if(exponent)
        }//the previous requires a lot of unwinding to understand it.
        return sign * (0x800000 + mantissa) * llPow(2.0, exponent - 150);
        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 ((string)a == "-0.000000") * 0x80000000;//for grins, detect the sign on zero. it's not pretty but it works.
}


    //denormalized range
float iuf(integer a)
     return sign * mantissa * llPow(2.0, -149);
{//union integer to float
}</lsl>
     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));
}
</source>


: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.
::The version with multiply turned out to work fine in a sample of ~600M random numbers, so yay! On the bad news side, the test program you asked me to run returns 0. As I said, 1p-127 returns 0 already, so no surprise there, but I tested this:
<source lang="lsl2">
default
{
    state_entry()
    {
        llOwnerSay((string)(0.0 != (float)"0x0.ffffffp-126"));
        llOwnerSay((string)(0.0 != (float)"0x1.0p-126"));
    }
}
</source>
::and got 0 and 1. So I'm afraid hex notation refuses to produce any denormal at all. <strike>A strategy you can maybe use for denormals is to treat them as a special case multiplying them by a power of two, so they come into a tractable range. But I haven't checked the source so I may be off here.</strike> (Oops, got your point now - there may be a way using part of the strategy I mention, I'll think about something)


: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).
::As for optimizations, I like your "risky" rewriting of llGetSubString, and of course it works. I know mults are slower than shifts (if that's what the code is compiled to in the end, which is questionable), but the difference, just maybe ten cycles or so, is really unnoticeable when there's a whole burden of task switching and other stuff going on. For that reason I tend to always optimize for memory. In the above case, it will matter little if the function takes 30000 or 30010 cycles to execute, just as an example to make my point (I'm sure the actual numbers will be different). Same with minus sign: the extra CPU burden will be insignificant (1 cycle probably). I've tried to test these differences but I never got conclusive results because noise level exceeds signal level by far, which just proves how insignificant the difference is. I tend to run out of space in scripts so I mostly optimize for memory. While I get to 60K+ code size often, I have yet to run into an application where shoving cycles that way gains anything noticeable. Also, I started to test this for Omei because she intended to use it in a memory-tight application. The space savings can make a difference.


:Lets look at the following function (I just wrote this and haven't had time to test it):
::The int-to-float implicit conversion seems to add an explicit cast, as an int constant is 6 bytes, a float constant is 10, and an integer implicitly or explicitly cast to float takes 7. By the way, -~-x is a saving vs. 1-x and vs. 1+-x (1 byte per unary sign vs. 6 bytes the integer constant, 8 the subtraction and 4 the addition). I have Mono size measurements here: [[User:Pedro Oval/Mono code memory usage]].
<lsl>integer FloatDistance(float a, float b, integer distance){
    //If you want to treat -0 and 0 as the same number replace "-~" with "+"
    integer A = fui(a);
    integer B = fui(b);
    if((A ^ B) & 0x80000000)
        A = (A & 0x7fFfFfFf)-~(B & 0x7fFfFfFf);
    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>
:It works on the idea of measuring the distance between two floats by looking at how many values lie between them. I'm not sure why I chose to use the formulation I did in FloatCompare and not this, the difference is that the above formulation weights changes in exponent very differently. FloatCompare treats changes is exponent very seriously it really screws with the distance, FloatDistance pays no special attention to it. Reguardless, most arithmetic corruption you notice by the time you test for it trashes more than one bit, so really you want to give it a distance of atleast 3, 7, or maybe even 15. I did contemplate allowing the user to specify the number of trashed bits instead of a distance but decided that was too narrow minded.


:These functions exist in my userspace instead of in an article because they are from a certain perspective, abandon-ware, I never really intended anyone else to use them. Not all of this code was written because it was a good idea, some of it was written because I was curious if I could, sometimes to find out just how bad an idea it was. You use them at your own risk. They aren't documented particularly well, they aren't even LSLEditor Safe. Hell they even had letter variables instead of proper names. Maybe I should add a disclaimer about the close proximity of dragons? This code was written during an optimization phase or mine (the tightness in the source is due to the 16KiB memory constraint of LSO).
::As for the 1e-45, OK, though I like it because it also reflects the loss of precision that happens in denormals. But it's my opinion that using the whole expansion in the comparison to detect a NaN is overkill, as any number between 1e-45 and 2.35e-38 will do. For example, 1e-40 is equally valid and doesn't clobber the source so much. --[[User:Pedro Oval|Pedro Oval]] 19:36, 17 February 2013 (PST)


:You complain about the code (with reason) and you accuse me of plagiarism but I'm actually a bit flattered that you would give the function a whirl. Despite your contempt and loathing for me, you still tried to use the code. Honestly I've never seen code like it, I didn't plagiarize it. There is no reason for anyone in the real world to have written anything like it, it required a contrived situation where you can't just <code>union{ float f; int i; }</code> or do a pointer typecasting. The whole concept of implementing a function to do this is a terribly bad idea. The creation of this function required two things: my willingness to write this insane function and my unwillingness to just submit a patch to LL to add this to the LSL compiler (LSO keeps no type meta-data in for the stack so you can pop an integer as a float and vice versa). The optimization was for fun.
::Alas, it's worse than I thought. 1.17e-38 (a denormal) works fine, but (float)"1.17e-38" returns 0. Not sure when did glibc stop supporting denormals. --[[User:Pedro Oval|Pedro Oval]] 19:59, 17 February 2013 (PST)


:But again, thanks for showing interest in it. It was some of my favorite code to write. It was a challenge. I'll think about how to best add infinity and nan support to them. -- '''[[User:Strife_Onizuka|Strife]]''' <sup><small>([[User talk:Strife_Onizuka|talk]]|[[Special:Contributions/Strife_Onizuka|contribs]])</small></sup> 23:14, 24 May 2012 (PDT)
:::I've put -~- back. I can't thank you enough for all your testing. I totally agree, optimizing for size is the main concern. I didn't remember this about logarithms that log<sub>a</sub>b * log<sub>b</sub>a = 1. -- '''[[User:Strife_Onizuka|Strife]]''' <sup><small>([[User talk:Strife_Onizuka|talk]]|[[Special:Contributions/Strife_Onizuka|contribs]])</small></sup> 21:38, 17 February 2013 (PST)


::No, I'm not accusing you. I was curious what would happen if somebody would take you seriously and use this in a build (and I'm pretty sure plenty take you seriously given how much gas you blow). It never really crossed my mind to use your code in my projects (I make it policy to not include code that has magic numbers even if it means making my scripts less "efficient") and I have never really traced it to see what exactly you are doing. Obscurity to me means very little - it's only part of the job to create something and the hardest part is to show people what you mean with it. I did however see you toss this code at Linden and ask why something else does not work which relies on it. So, I was really curious what others around me are doing... I was a bit surprised... And I guess that having goons jump at you, is a good reason for anybody not to contribute and invest their talent in something other than the Second Life wiki.
:::Crap. Both Float2Sci and Float2Hex are screwed. Without having access to the source for the Mono compiler we can't be sure it's glibc or Mono, though we can confirm it's glibc that's bugged by checking how it works with LSO. I may just have to manifest at an upcoming [[Simulator_User_Group|Simulator]]/[[Server_Beta_User_Group|Server_Beta]] User Group meeting and complain. should open up a ticket on jira first. hmmm. -- '''[[User:Strife_Onizuka|Strife]]''' <sup><small>([[User talk:Strife_Onizuka|talk]]|[[Special:Contributions/Strife_Onizuka|contribs]])</small></sup>


::If you look my articles you can still see that I must justify each part of my code and pulling of tricks like telling people what ''it does not relate to'' instead of offering the formalities behind it never accomplishes anything - well, I guess you could get a gang together and file Jira reports based on some false sense of vanity and prestige. I mean at this rate, at least I know what I can answer when somebody asks me "why do I have lag?" / "why doesn't stuff work in SL?" etc...
::::Duh, good point. It's a Mono-only quirk. (float)"0x1p-127", (float)"0x1p-149", and (float)"1e-45" are all nonzero in LSO, and the latter two are equal to 1e-45 as expected. About the latest change, I fail to see the gain in moving the sign of c to the <code>-~-llFloor</code> line, as that forces a subtraction later which eats 4 extra bytes. Could it not remain as <code>integer c = ~-llFloor...</code>, then <code>...llPow(2, -c))))) + c)</code>? --[[User:Pedro Oval|Pedro Oval]] 20:23, 18 February 2013 (PST)


::Don't get me '''too wrong''' on the loathing, you started a thread with, let's be fair and quote so you don't get the false impression that I am making stuff up:
:::::Of course it can but why should it make a difference as we have just moved the negation further down. Am I missing something? Please tell me that subtraction isn't more expensive than addition. -- '''[[User:Strife_Onizuka|Strife]]''' <sup><small>([[User talk:Strife_Onizuka|talk]]|[[Special:Contributions/Strife_Onizuka|contribs]])</small></sup> 21:47, 18 February 2013 (PST)


:: [https://jira.secondlife.com/browse/ARVD-41?|"Wiki: Deny all content deletion requests made by Kira Komarov and restore all content"]
::::::Sorry to bring bad news but yes: addition of two local integers takes 4 bytes, as does multiplication. But subtraction takes 8, as do division, mod and shift. "!" takes 3 bytes, while "~" and "-" take 1 each. Take a look at the table I linked to. You'll want to cry. As for the several variants you enumerate below, regrettably, float to integer cast takes 5 bytes, which is more than leaving it as <= instead of < (3 extra bytes). I think function calls need at least as many bytes as their name, so I wouldn't go that route. Not 100% sure there though. Converting x-y to x+-y saves 3 bytes. --[[User:Pedro Oval|Pedro Oval]] 14:31, 19 February 2013 (PST)


::Which is a sign of disrespect... I am unsure whether that disrespect is aimed at me, at my work or at yourself. Quite frankly, I do not mind if you delete your functions because they do not work, you are busy "with work" as you posted on my talk page or for whatever other reason - they are yours, just because you barfed them here, will not make me trod into them like an elephant and rip them apart or, like [[User:Void Singer|Void SInger]] copy them and redistribute them "just to annoy you". However, I did not expect you to violate your integrity to go that far into ... peasantry to create a Jira (which, as I've seen countless collaborative bug-systems) are really just meant for bug reports and "serious" issues. I hope that satisfies your statement and you can see that I do not really "hate" you...
:Oh yeah, ((integer)x - (3 <= x)) is equivalent to...
:* (integer)x - (2 < (integer)(x))
:* (integer)x - (((integer)(x)) == 3)
:* (integer)x + (((integer)(x)) / -3)
:* (integer)x - !(3 & ~(integer)(x))
:* (integer)x - (2 < llFloor(x))
:* (integer)x - ((llFloor(x)) == 3)
:* (integer)x + ((llFloor(x)) / -3)
:* (integer)x - !(3 & ~llFloor(x))
: the value of x will always be in the range [0.5, 4). Truncating or rounding it down to an integer result in it being in the range [0, 3], we want to further truncate this range to [0, 2]. -- '''[[User:Strife_Onizuka|Strife]]''' <sup><small>([[User talk:Strife_Onizuka|talk]]|[[Special:Contributions/Strife_Onizuka|contribs]])</small></sup> 22:33, 18 February 2013 (PST)


::The problem with people like you is that you are '''90% mouth and 10% essence''' - I just wanted a confirmation. I mean, your self-respect is so low that you open up such a thread when most of your scripts are broken. I wouldn't have minded that somebody with lots of contributions would have requested from me not to remove my own content, or had a chat with me about some bug they have noticed. However, that a person like you drives and Internet form/wiki war, is more sad than it is comprehensible.  
:* (integer)x + (integer)(x * -0.3333333432674407958984375)
:* (integer)x - (2.9999997615814208984375 < x))


::You like the wiki (I like it too) and you want it to be used in collaborative environments, but how could I collaborate with you under such circumstances? Should I back you up and we fight together, senseless wars, against people contributing to this wiki? I tell you what, if together we team up, I promise you that in about one week we will have Linden dangling by their noses and we will thwart off '''any''' contribution to this wiki. I'm sorry, but that's not my sense of fun...
:Another reason for the excessive precision, I test a lot of code in LSLEditor, which uses doubles. If I want things to work I have to keep my constants in double precision land. I calculate most of them with the windows calculator and then just copy and paste. That way when I paste them back into the windows calculator I can work the math backwards and figure out where the constants came from. -- '''[[User:Strife_Onizuka|Strife]]''' <sup><small>([[User talk:Strife_Onizuka|talk]]|[[Special:Contributions/Strife_Onizuka|contribs]])</small></sup> 23:43, 18 February 2013 (PST)


::On the other hand, I think that I have met you in-world on an alt. I'm not sure if it was you or not (I personally use my main avatar from everything from sex to conferences, not because it's smart, but because I really do not care <-- try this sentence) because we discussed some intricate coding issues with Quick Collar. If that was really your alt, you should know by now that I am actually very collaborative, open and if it's not the funny articles also fun and easy to get along with.
== too many (, or not enough ) ==


::That being said, I really wanted to see what you're made of. I mean, I am very aware and I have seen some people ''in other environments'' that have truly amazing skills and knowledge and who not only surpass mine but they are so good they make me blush. However, I see now that when you wage war against a person like me, your words come easy... When, instead you are attacked and held responsible you cower inside yourself. It's a pity really, having had that "covert" meeting we had, I think we could have even got along finely...
<code>
return (0x3FF & (integer)(a * (0x800 >> b))) | (((c + (b = ((integer)a - (3 <= (a *= (0.0000152587890625 * (0x40000000 >> c)))))) << 10 ) | b);
</code>


::I still cannot understand why you would create a a Jira for that when, in essence, it has nothing to do with you...
count("(") != count(")") [[User:Cron Stardust|Cron Stardust]] 07:04, 27 June 2013 (PDT)


::Kira Komarov 12:07, 25 May 2012 (PDT)
:I think that's a left over from a last minute refactoring. Fixed. -- '''[[User:Strife_Onizuka|Strife]]''' <sup><small>([[User talk:Strife_Onizuka|talk]]|[[Special:Contributions/Strife_Onizuka|contribs]])</small></sup> 20:36, 27 June 2013 (PDT)


== P.S. ==
:: Thanks! :D [[User:Cron Stardust|Cron Stardust]] 20:55, 27 June 2013 (PDT)


Thank you for explaining. But spare your energy and post what you have explained to me right here where you advertise your function. It's actually a very good explanation.
== Indeterminates ==


... Don't feed it to me though, thanks. I can handle it myself.
There's one more value that can be encoded and read back: indeterminate. Indeterminate values have a representation of FFC00000, see http://babbage.cs.qc.edu/courses/cs341/IEEE-754references.html#kevin_chart. They can be read through llList2CSV and they show as -nan. <source lang="lsl2">
default
{
    state_entry()
    {
        float a=(float)"nan";
        llOwnerSay(llList2CSV(["(float)\"nan\"", a]));
        a=-a;
        llOwnerSay(llList2CSV(["-a", a]));
        a=(float)"inf"*0;
        llOwnerSay(llList2CSV(["inf*0",a]));
        a=llAcos(2);
        llOwnerSay(llList2CSV(["acos(2)",a]));
    }
}
/*outputs:
[08:05:05] Object: (float)"nan", nan
[08:05:05] Object: -a, -nan
[08:05:05] Object: inf*0, -nan
[08:05:05] Object: acos(2), nan
*/</source> (float)"-nan" does not work :( --[[User:Sei Lisa|Sei Lisa]] ([[User talk:Sei Lisa|talk]]) 08:06, 6 September 2015 (PDT)


Kira Komarov 12:15, 25 May 2012 (PDT)
:I think I knew (float)"-nan" was borked, can't really do anything about it. I guess I'll multiple +/-inf by zero or one for the -nan generation. -- '''[[User:Strife_Onizuka|Strife]]''' <sup><small>([[User talk:Strife_Onizuka|talk]]|[[Special:Contributions/Strife_Onizuka|contribs]])</small></sup> 17:05, 6 September 2015 (PDT)


== Ever considered making a fui16? ==
::It should be better now. Haven't compiled it. Probably should. No time now. Half-precision won't work right, it needs some TLC. -- '''[[User:Strife_Onizuka|Strife]]''' <sup><small>([[User talk:Strife_Onizuka|talk]]|[[Special:Contributions/Strife_Onizuka|contribs]])</small></sup> 17:45, 6 September 2015 (PDT)


I was just looking around for how I could union a float into a 16 bit integer, perhaps using the [http://en.wikipedia.org/wiki/Half-precision_floating-point_format IEEE 754-2008 half-precision] format. Yes, I know there would be losses, and I also know that SL olny has 32 bit integers. The final goal for my application is to pack two floats into a single integer, via bitshifting the first into the high range and then ORing with the second, a la:
:::Thanks a lot! fui is working perfectly, but iuf can't produce nan, only -nan. If it helps, I've found that while (float)"-nan" doesn't work, -(float)"nan" and -1*(float)"nan" do.--[[User:Sei Lisa|Sei Lisa]] ([[User talk:Sei Lisa|talk]]) 18:34, 6 September 2015 (PDT)
<lsl>
:::Correction. (-1)*(float)"nan" doesn't produce -nan. The parentheses were important.--[[User:Sei Lisa|Sei Lisa]] ([[User talk:Sei Lisa|talk]]) 08:21, 9 September 2015 (PDT)
// To pack:
float a = PI;
float b = 4.2;
integer result = (fui16(a) << 0x10) | fui16(b);


// To unpack:
::::That makes some perverse sense. I'll log in sometime this weekend and figure out a way to do this (hopefully without using a branch). -- '''[[User:Strife_Onizuka|Strife]]''' <sup><small>([[User talk:Strife_Onizuka|talk]]|[[Special:Contributions/Strife_Onizuka|contribs]])</small></sup> 15:26, 12 September 2015 (PDT)
float c = i16uf(result >> 0x10); // Might need to perform some further magic here to account for the fact that the sign bit doesn't shift.
float d = i16uf(result & 0xFFFF);
</lsl>
Of course, in the general case one can't make assumptions about range and resolution. That said, in my application I ''can'' make such assumptions, and I'm just going to hack together something that works based on those assumptions.  I'm just here feeding the fetish that resulted in these very-useful-to-me functions (fui, iuf) in the first place. >:D


{{Unsigned|Cron Stardust}}


:Lets see, I could either work on [[Template:LSL Function]] et al as to split the notes section in half, or I could write fui16 a most clever function. Hmmm. Fun clever coding or Template drudge. Half precision doesn't have the same range, grrrr... -- '''[[User:Strife_Onizuka|Strife]]''' <sup><small>([[User talk:Strife_Onizuka|talk]]|[[Special:Contributions/Strife_Onizuka|contribs]])</small></sup> 20:57, 24 December 2012 (PST)


:Upon second though, I really don't want to dig into floating point math at the moment. Mostly you just need to add clipping to fui, change the constants and add infinity support to iuf. With clipping implemented the error correcting code can be removed. Here is my first crack at updating the constants. I've not tested it. -- '''[[User:Strife_Onizuka|Strife]]''' <sup><small>([[User talk:Strife_Onizuka|talk]]|[[Special:Contributions/Strife_Onizuka|contribs]])</small></sup> 21:35, 24 December 2012 (PST)
== FUI2HexFloat ==
This fuction can t be compiled .
Error at the line if(e ^ 127)
e is a variable declared later


<lsl>
[[User:Miranda Umino|Miranda Umino]] ([[User talk:Miranda Umino|talk]]) 11:28, 14 September 2015 (PDT)
integer fui16(float a)//Mono Safe, LSO Safe, Doubles Unsupported, LSLEditor Unsafe
{//union float to integer
    if((a)){//is it non zero?
        integer b = (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 > 65504)//Round up to infinity
            return b | 0x7C00;//Positive or negative infinity
        integer c = llFloor((llLog(a) / 0.69314718055994530941723212145818));//extremes will error towards extremes. following yuch corrects it.
        return (0x3FF & (integer)(a * (0x800 >> b))) | (((c + 14 + (b = ((integer)a - (3 <= (a /= (float)("0x1p"+(string)(c -= ((c >> 31) | 1)))))))) << 10 ) | b);
    }//for grins, detect the sign on zero. it's not pretty but it works. the previous requires a lot of unwinding to understand it.
    return ((string)a == (string)(-0.0)) << 15;
}


float iuf16(integer a)
:it should compile now but it needs testing. --'''[[User:Strife_Onizuka|Strife]]''' <sup><small>([[User talk:Strife_Onizuka|talk]]|[[Special:Contributions/Strife_Onizuka|contribs]])</small></sup> 13:39, 14 September 2015 (PDT)
{//union integer to float
    return ((float)("0x1p"+(string)((a | !a) - 25))) * ((!!(a = (0x1f & (a >> 10))) << 10) | ((a & 0x7ff))) * (1 | ((a << 16) >> 31));
}
</lsl>

Latest revision as of 04:44, 19 September 2015

Ever considered making a fui16?

I was just looking around for how I could union a float into a 16 bit integer, perhaps using the IEEE 754-2008 half-precision format. Yes, I know there would be losses, and I also know that SL olny has 32 bit integers. The final goal for my application is to pack two floats into a single integer, via bitshifting the first into the high range and then ORing with the second, a la:

// To pack:
float a = PI;
float b = 4.2;
integer result = (fui16(a) << 0x10) | fui16(b);

// To unpack:
float c = i16uf(result >> 0x10); // Might need to perform some further magic here to account for the fact that the sign bit doesn't shift.
float d = i16uf(result & 0xFFFF);

Of course, in the general case one can't make assumptions about range and resolution. That said, in my application I can make such assumptions, and I'm just going to hack together something that works based on those assumptions. I'm just here feeding the fetish that resulted in these very-useful-to-me functions (fui, iuf) in the first place. >:D

—The preceding unsigned comment was added by Cron Stardust

Lets see, I could either work on Template:LSL Function et al as to split the notes section in half, or I could write fui16 a most clever function. Hmmm. Fun clever coding or Template drudge. Half precision doesn't have the same range, grrrr... -- Strife (talk|contribs) 20:57, 24 December 2012 (PST)
Upon second though, I really don't want to dig into floating point math at the moment. Mostly you just need to add clipping to fui, change the constants and add infinity support to iuf. With clipping implemented the error correcting code can be removed. Here is my first crack at updating the constants. I've not tested it. -- Strife (talk|contribs) 21:35, 24 December 2012 (PST)
integer fui16(float a)//Mono Safe, LSO Safe, Doubles Unsupported, LSLEditor Unsafe
{//union half-precision float to short integer 
    if((a)){//is it non 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 > 65504.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 *= (0.0000152587890625 * (0x40000000 >> c))))) << 10 ) | b);
    }//for grins, detect the sign on zero. it's not pretty but it works. the previous requires a lot of unwinding to understand it.
    return (integer)((string)a == (string)(-0.0)) << 15;
}

float i16uf(integer a)
{//union short integer to half-precision float
    if((a & 0x7C00) == 0x7C00)
        return (1 | ((a << 16) >> 31)) * (float)"Infinity";
    return 0.000000059604644775390625 * (1 << (a - !!a)) * ((!!(a = (0x1f & (a >> 10))) << 10) | ((a & 0x3ff))) * (1 | ((a << 16) >> 31));
}
// To pack:
float a = PI;
float b = 4.2;
integer result = (fui16(a) << 16) | fui16(b);
 
// To unpack:
float c = i16uf(result >> 16); // No magic needed, sign bit is handled correctly.
float d = i16uf(result);
Just tested in SL with the following results: Had to place a closing paren in the first return statement of iuf16, I placed it just before the mult as I suspect that's what was intended. I wrote a simple ownersay of the existing test code which gave the following as the output: Cron Stardust 16:12, 25 December 2012 (PST)
  a = 3.141593 = 0xC90FDBp-22
= c = 3.140625 = 0xC9p-6
  b = 4.200000 = 0x433333p-20
= d = 4.199219 = 0x433p-8
Further testing, looking for error cases, resulted in a lot of good: the functions seem to operate as they should. I tested using all the constants demonstrated on the Wikipedia article and only one failed to match perfectly: 0.0000000596046 doesn't encode to 0x0001, instead it results in 0x0000. All other values seem to work great. Cron Stardust 22:00, 25 December 2012 (PST)
Also tested that with a = -2.0 and b = -2.0 that everything works out: no issues happen with the sign bit status that I could detect - so I took the liberty of changing the comment in your code. Cron Stardust 22:34, 25 December 2012 (PST)
I'm willing to bet it's a lack of precision in their approximation. Try: 0.000000059604644775390625 -- Strife (talk|contribs) 00:36, 26 December 2012 (PST)
I'm not 100% sure of the "llFloor((llLog(a) / 0.6931471...)" line but I think in this range it's accurate and we don't need to do any correction. The only way to know for sure is to try every value in the range [0.0001220703125, 65504.0]. I know it's not going to overflow so I've removed the overflow protection code. If you want to do that testing, here is the return line you would use which assumes that "c" is accurate: return (0x3FF & (integer)((a / (float)("0x.004p"+(string)c)))) | (((c + 15) << 10 ) | b); It has the added bonus of making fui16 LSLEditor safe. This was also a good demonstration of how the layers of correction code were bolted on. -- Strife (talk|contribs) 00:50, 26 December 2012 (PST)

This is a better version of what I was talking about but it needs extensive testing. If c is not accurate this will not work.

		integer c = llFloor((llLog(a) / 0.69314718055994530941723212145818)) + 15;//extremes will error towards extremes. No corrections are attempted.
		return (0x3FF & (integer)((a * 0.03125 * (0x40000000 >> c)))) | ((c << 10) | b);

I've gone back and removed my string-float pow code since it can all be done with integers and float math. Faster this way. Less stressful for the sim. -- Strife (talk|contribs) 01:32, 26 December 2012 (PST)

Testing

Just run the following code and it will tell you if c is accurate for the entire half-precision range (this is one of the things iuf is really good for, iterating over every float value). -- Strife (talk|contribs) 02:55, 26 December 2012 (PST)

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 stride 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 a lot 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 warranted.

integer test(float a)//Mono Safe, LSO Safe, Doubles Unsupported, LSLEditor Unsafe
{//union half-precision float to short integer 
    if((a))
    {//is it non zero?
        if((a = llFabs(a)) < 0.0001220703125)//Denormalized range check & last stride of normalized range
            return FALSE;//the math overlaps; saves cpu time.
        if(a > 65504.0)//Round up to infinity
            return FALSE;//Positive or negative infinity
        integer c = llFloor((llLog(a) / 0.69314718055994530941723212145818)) + 14;//extremes will error towards extremes. following yuch corrects it.
        return (integer)(a *= (0.0000152587890625 * (0x40000000 >> c))) - 1;
    }//for grins, detect the sign on zero. it's not pretty but it works. the previous requires a lot of unwinding to understand it.
    return FALSE;
}

float i16uf(integer a)
{//union short integer to half-precision float
    if((a & 0x7C00) == 0x7C00)
        return (1 | ((a << 16) >> 31)) * (float)"Infinity";
    return 0.000000059604644775390625 * (1 << (a - !!a)) * ((!!(a = (0x1f & (a >> 10))) << 10) | ((a & 0x3ff))) * (1 | ((a << 16) >> 31));
}

list gBadBlockCounts;

default
{
    state_entry()
    {
        llOwnerSay("beginning test");llResetTime();
        
        integer badCount = 0;
        integer badStart;
        
        integer start = fui(i16uf(1) / 2.0);//start just before min value, these will get clipped to zero.
        integer end = fui(65536.0);//stop just after infinity
        integer i;
        for(i = start;i < end; i++)
            if(test(iuf(i)))
            {
                llSetText("bad:  "+(string)i, <1.0, 0.0, 0.0>, 1.0);
                if (badCount++ <= 0)
                {
                    badStart = i;
                    llOwnerSay("Found bad number: " + (string) i);
                }
            }
            else
            {
                llSetText("good: "+(string)i, <0.0, 1.0, 0.0>, 1.0);
                if (badCount > 0)
                {
                    llOwnerSay("Total of " + (string) badCount + " numbers, ending with " + (string) (i-1));
                    gBadBlockCounts = [badStart, i - 1] + gBadBlockCounts;
                    badCount = 0;
                }
            }
        llSetText("test complete.\ntook " + (string) llGetTime() + " secs over " + (string) (end - start) + " values\nsaw " + (string) llGetListLength(gBadBlockCounts) + " bad blocks.\nTouch for list of bad.", <0.0, 1.0, 1.0>, 1.0);
        state printout;
    }
    
}

state printout
{
    touch_end(integer index)
    {
        while (--index >= 0)
        {
            if (llGetListLength(gBadBlockCounts))
            {
                llRegionSayTo(llDetectedKey(index), 0,
                    "Bad blocks strided list, using stride pattern (block_start, block_end):"
                );
                llRegionSayTo(llDetectedKey(index), 0,
                    llDumpList2String(gBadBlockCounts, ", ")
                );
            }
            else
            {
                llRegionSayTo(llDetectedKey(index), 0,
                    "Nothing to report."
                );
            }
        }
    }
}
Tested this morning. Went bad at values 864026624 through 900508790. Not sure if there were further values: I set it up to run and then wen to work for the day, when I came back it had been yelling for a very long time... :P Will see what I can do, however while I can understand the code, I've not yet been able to get my mind in deep enough to understand the algorithm. Cron Stardust 18:55, 26 December 2012 (PST) EDIT: Just noticed that there was a function named test in there that wasn't used: I'd pasted in the fui16 function from above to make the code compile and run. That may have invalidated the test, so I've going to retest usung the "test" function in place of the fui16 call in state_entry. I've edited the above state_entry to reflect my current code.
Just passed the 864026624 mark without errors, so yes: my previous test was invalid. Continuing testing - just like GLaDOS likes. Cron Stardust 20:59, 26 December 2012 (PST)
I should have thought of this sooner, we don't need to test every value, just the ones who's values surround where the value of "c" changes. We only need to test ummm 84 values... just the powers of two and the immediate lesser values. -- Strife (talk|contribs) 01:44, 27 December 2012 (PST)
integer fui(float a)//Mono Safe, LSO Safe, Doubles Unsupported, LSLEditor Unsafe
{//union float to integer
    if((a))
    {//is it non zero?
        integer b = (integer)(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 stride 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.
        a /= (float)("0x1p"+(string)(c -= ((c >> 31) | 1)));
        integer bb = ((integer)a - (3 <= a));
        return (0x7FFFFF & (integer)(a * (0x1000000 >> bb))) | (((c + 126 + bb) << 23 ) | b);
    }//for grins, detect the sign on zero. it's not pretty but it works. the previous requires a lot of unwinding to understand it.
    return (integer)((string)a == (string)(-0.0)) << 31;
}

float iuf(integer a)
{//union integer to float
    integer aa = 0xff & (a >> 23);
    return ((float)("0x1p"+(string)((aa | !aa) - 150))) * ((!!aa << 23) | ((a & 0x7fffff))) * (1 | (a >> 31));
}//will crash if the raw exponent == 0xff; reason for crash deviates from float standard; though a crash is warranted.

integer get_log2(float a)
{//we use the natural log because testing showed it's more accurate than log10.
    return llFloor((llLog(a) / 0.69314718055994530941723212145818));//must be divide, multiplying the reciprocal adds extra corruption.
}

integer test_log2(integer power)
{
    float a = (float)("0x1p"+(string)power);
    float b = iuf(fui(a) - 1);//The underflow is in our favor and does what we want.
    integer c = get_log2(a);
    integer d = get_log2(b);
    integer e = (c == power) && ((d + 1) == power);
//    llOwnerSay(llList2CSV([power, c, d, Float2Hex(a), Float2Hex(b), int2hexdword(fui(a)), int2hexdword(fui(b)), e]));
    return e;
}

default
{
    state_entry()
    {
        integer start = -25;
        integer end = 17;
        integer i;
        for(i = start; i <= end; i++)
            if(!test_log2(i))
                llOwnerSay((string)i);
    }
}
Ok, so executed - though I'm leaving the other script running, and it seems good thus far; will report when it completes. The results from this new test are that I saw values reported in the following ranges: [-25, -16], -14, [-12, -2], [3, 17] -- Cron Stardust 19:14, 27 December 2012 (PST)
Well that is bad. Guess c is not accurate so we can't remove the correction code (the overflow code can still be removed). I wonder what happens if you feed it NaN. Probably need a special case just for that. -- Strife (talk|contribs) 20:45, 27 December 2012 (PST)
I remembered how the correction code works and I fused one of the moving parts. I don't think it's necessary for this range but I'm adding it back in. Since "c" could be off by one in either direction, this gives us three possible outcomes when generating new-a.
  1. It's too small, the result is that new-a is in range [0.5, 1)
  2. C is correct, new-a is in range [1, 2)
  3. It's too big, the result is that new-a is in range [2,4)
Solution? Map these three outcomes to three consecutive values so that they can be used to adjust c. Truncating new-a gives us [0.5, 1)->0, [1, 2)->1, [2,4)->{2,3}. Yes they are consecutive but we need to map the final set to a single value. Hence the subtract by (3 <= a).
It's a shame I never documented which edge cases this code fixed. It would take a lot of the guess work out. -- Strife (talk|contribs) 21:22, 27 December 2012 (PST)
For what it's worth, the old test code finally completed! Took about 6-8 days continuous* calculation in my home region. (* script reported 521479.8 seconds, but I'd had to stop and fix a UI error after several hours of computation, and picked up where I'd left off.) Here's the report: Cron Stardust 18:14, 5 January 2013 (PST)
Bad blocks list, using stride pattern [block_start, block_end]:
[
 [1191182336, 1191182336], [1182793721, 1182793727], [1174405120, 1174405120], [1166016505, 1166016511], 
 [1157627901, 1157627903], [1149239293, 1149239295], [1140850685, 1140850687], [1132462077, 1132462079], 
 [1124073469, 1124073471], [1115684861, 1115684863], [1107296255, 1107296255], [1098907647, 1098907647], 
 [1090519039, 1090519039], [1048575999, 1048575999], [1040187390, 1040187391], [1031798782, 1031798783], 
 [1023410174, 1023410175], [1015021564, 1015021567], [1006632956, 1006632959], [ 998244348,  998244351], 
 [ 989855740,  989855743], [ 981467132,  981467135], [ 973078524,  973078527], [ 964689912,  964689919]
]

fui support for NaN and Inf

I must be one of the few people who have found an actual use to fui :) I'm using it for compact lossless transmission of floats over HTTP to a PHP server in base 64 (after removing the == and the trailing A's), and I decode it in the server using unpack() and some string manipulation. I would love to see fui support NaN, Inf and -Inf, but more for the sake of completion than because I have a real use (except possibly debugging). Maybe it's as straightforward as adding this on top?

  if ((string)a == "Infinity") return 0x7F800000;
  if ((string)a == "-Infinity") return 0xFF800000;
  if ((string)a == "NaN") return 0x7FC00000; // to choose one

I guess there's no way to extract the actual bits of a NaN, or is there? --Pedro Oval 05:09, 26 December 2012 (PST)

Oops, I've corrected "Inf" to read "Infinity" above. --Pedro Oval 07:58, 26 December 2012 (PST)

Sure, I don't see why not. I'll think about ways of doing it (I'm not sure if successive tests is really the best way of doing so). The reason Infinity was never handled was that LSO doesn't allow it. These functions were originally conceived before Mono was on the horizon (at the time I had wanted to build an LSO bytecode emulator in LSL). -- Strife (talk|contribs) 01:01, 27 December 2012 (PST)
It's done. -- Strife (talk|contribs) 22:43, 27 December 2012 (PST)
You will be pleased (or horrified) to see that Float2Hex and Float2Sci both support NaN and Infinities now. -- Strife (talk|contribs) 00:22, 29 December 2012 (PST)

Alternate code

I was pondering alternate ways to b >> (13 + ((b & 0x3FFF) == 0x2000)) because it's just so ugly.

  • b >> (((0x3FFF & b ^ 0x1FFF) + 0x34001) >> 14) - we flip the low bits, so if all of them are on, that is to say our condition has been met, adding one will cause a carry into the 14th bit.
  • b >> ((0x38000 - (0x3FFF & b ^ 0x2000)) >> 14) - similar to above, we turn the high bit off, so if it's zero it doesn't cause a carry from above.
  • b / ((((0x3FFF & b ^ 0x1FFF) + 0x4001) & 0xC000) >> 1)

It's a shame there isn't a way to further improve it. It's an ugly mess that only gets uglier. ~_~ -- Strife (talk|contribs) 23:22, 28 December 2012 (PST)

Bugs in iuf and fui

Bug in iuf

iuf does not support exponent parts of 2-104 or below. The reason is that in an intermediate step, it is trying to calculate 0x1p-127 or below, which yields 0. For exaple, iuf(0x0bffffff) returns 0, while iuf(0x0c00000) returns the correct result.

Rather than trying to find a fix, I created my own iuf that seems to work, even if it's unoptimized and not very fast (though it contains a small trick to reduce the total number of iterations to a maximum of 25 or so):

float iuf(integer e)
{
    float result = e & 0x7FFFFF;
    integer sign = e & 0x80000000;
    e = ((e >> 23) & 0xFF) + -150;
    // deal with Inf/NaN
    if (e == 105) { if (result) return (float)"NaN"; if (sign) return (float)"-Inf"; return (float)"Inf"; }
    if (e ^ -150)
    {
        result += 0x800000;
        while (e > 9) { result *= 512; e += -9; }
        while (e < -9) { result *= 0.001953125; e += 9; }
        while (e > 0) { result *= 2; --e; }
        while (e < 0) { result *= 0.5; ++e; }
        if (sign) return -result;
        return result;
    }
    if (sign) return -(result*1e-45);
    return result*1e-45;
}

A fix might be along the lines of not applying the -23 offset in advance (as usage of -150 implies) but postmultiplying by 2^-23 instead. I haven't tried though. --Pedro Oval 09:37, 8 February 2013 (PST)


If the denormalized range isn't working then Float2Hex is likely broken too. hmmm -- Strife (talk|contribs) 11:11, 9 February 2013 (PST)
It's not just the denormal range, it affects the normal range too. 2-104 is small but not denormal; the denormal range starts at 2-127. --Pedro Oval 16:22, 9 February 2013 (PST)
I start the denormalized range a power of two early but if it's failing all the way up to -104... that's a big problem. I'll have to play with it. Multiplying by a power of two shouldn't trash the mantissa. -- Strife (talk|contribs) 20:29, 11 February 2013 (PST)
Maybe I misunderstood. If what you meant is that hex floating point format is unable to produce denormal numbers, then that's exactly the problem, yes. 0x1p-127 produces 0, even if the denormal range can represent numbers down to 0x1p-149. What I meant is that inputs to the function representing exponents of -104 or below failed (as these involved calculation of 0x1p-127 or below). Now some good news. The new function revised to use llPow works correctly for small exponents and seems to work correctly for the rest. Also, under Mono at least its performance is about 2.5 times that of the old (string operations are s-l-o-w). --Pedro Oval 06:24, 12 February 2013 (PST)

Bug in fui

Alas, iuf is not the only one with problems. For exponents of -2 or less, and mantissas in the vicinity of 0x7FFFFF, results are almost always wrong. Examples:

  • fui(iuf(0x017fffdd)) = 0x01ffffee (the first one failing with mantissa > 0x7FFF00, exponent is -124)
  • fui(iuf(0x017fffff)) = 0x01ffffff (the last one failing with that exponent; all in the middle also fail)
  • fui(iuf(0x01ffffc5)) = 0x027fffe2 (next one failing)
  • fui(iuf(0x01ffffff)) = 0x027fffff (last one with that exponent, all in the middle fail)
  • fui(iuf(0x037ffffd)) = 0x03fffffe (for this exponent the failure range is shorter - just 3 numbers)
  • fui(iuf(0x38ffffff)) = 0x38ffffff (for a few exponents there are no failures at all; this is an example)
  • fui(iuf(0x3e7fffff)) = 0x3effffff (last one failing)

I've manually checked some of the iuf conversions and they worked correctly with the code I posted above, so the problem is in fui. For example: if (iuf(0x25fffff6) == .44408894e-15) llOwnerSay("iuf ok"); says "iuf ok", but fui(.44408894e-15) gives 0x267ffffb, not 0x25fffff6. --Pedro Oval 11:48, 8 February 2013 (PST)

Are you doing these tests in Mono, LSO or both? -- Strife (talk|contribs) 10:48, 9 February 2013 (PST)
I tested the full range only in Mono, but a quick run seems to show that LSO is affected in exactly the same way. At least the range starts in the same number (0x017fffdd) and produces the same result (0x01ffffee). --Pedro Oval 16:31, 9 February 2013 (PST)
It will take me some time (maybe a few weeks) to get around to this, I'm very busy currently. -- Strife (talk|contribs) 11:14, 9 February 2013 (PST)
I admit to kicking off Pedro's investigation, as I tried to use iuf and fui in Mono and found that fui (iuf (x) ) gave zero for all values of x from 1 to 3 million, at which point when I gave up :) They seem to work a lot better in LSO from my briefer tests so far. Omei Qunhua 15:52, 9 February 2013 (PST)

Try these

I've swapped out the hack pow method for the real function. I'm hoping this will fix iuf.

I've also expanded the compressed lines so that they are easier to tinker with. I've enlarged the supported range of a in the error calculation, the range supported has changed from [0.5, 4) to [0.25, 4).

integer fui(float a)//Mono Safe, LSO Safe, Doubles Unsupported, LSLEditor Unsafe
{//union float to integer
    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
        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
        integer c = llFloor((llLog(a) / 0.69314718055994530941723212145818));//extremes will error towards extremes. following yuch corrects it.
        c -= (c >> 31) | 1;//this sets up an error towards the zero
        a /= llPow(2.0, c);//a should now be in the range [0.25, 4).
        integer d = (integer)a + (0.5 >= a) - (3 <= a);//This calculates the actual error. It does so by mapping the value of a to the error.
        //Specifically we take advantage of the where the normalization bit has been shifted.
        //[0.25, 0.5) == -2, [0.5, 1) == -1, [1,2) == 0, [2, 4) == 1 ~ note the actual values of d are off by two.
        llOwnerSay(llList2CSV([0x800000 == (0xF800000 & (integer)(a * (0x2000000 >> d))), c, d, a, (a < 0.25) || (a > 4)]));//first value tells you if you got d right or wrong.
        //The check works by seeing where the normalization bit has been shifted. The normalization bit gets chopped off with 0x7FFFFF.
        return (0x7FFFFF & (integer)(a * (0x2000000 >> d))) | (((c + 125 + d) << 23 ) | 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)) << 31;//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 0x7FFFFFFF;//NaN time! We have no way to tell NaN's apart so lets just choose one.
}

float iuf(integer a)
{//union integer to float
    if((a & 0x7F800000) == 0x7F800000)
        return (1 | (a >> 31)) * (float)llList2String(["NaN","Infinity"], !(a & 0x7FFFFF));
    integer b = 0xff & (a >> 23);
    return llPow(2.0, (b | !b) - 150) * (((!!b) << 23) | ((a & 0x7fffff))) * (1 | (a >> 31));
}//will crash if the raw exponent == 0xff; reason for crash deviates from float standard; though a crash is warranted.
The above fails at every number from the range I tested (mantissa in the vicinity of 0x3FFFFF, exponent -2 and below). --Pedro Oval 06:24, 12 February 2013 (PST)
More specifically, it seems to fail for all numbers with bit 22 set (mask 0x00400000). --Pedro Oval 16:53, 13 February 2013 (PST)

This uses a modified error correction code. I'm not sure if it will be better or worse. It has the potential to run out the high side of the stated bounds on a but I don't think this will be the case. We shall have to see.

integer fui(float a)//Mono Safe, LSO Safe, Doubles Unsupported, LSLEditor Unsafe
{//union float to integer
    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
        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
        integer c = llFloor((llLog(a) / 0.69314718055994530941723212145818));//extremes will error towards extremes. following yuch corrects it.
        --c;//Make it error down. Hopefully this won't shift anyone into the [4, 8) range.
        a /= llPow(2.0, c);//a should now be in the range [0.5, 4), we assume no error is added.
        integer d = (integer)a - (3 <= a);//This calculates the actual error. As it turns out, the integer portion of a is {0,1,2,3} the errors in c are {-1,0,1,1}
        llOwnerSay(llList2CSV([0x800000 == (0xF800000 & (integer)(a * (0x2000000 >> d))), c, d, a]));
        return (0x7FFFFF & (integer)(a * (0x1000000 >> d))) | (((c + 126 + d) << 23 ) | 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)) << 31;//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 0x7FFFFFFF;//NaN time! We have no way to tell NaN's apart so lets just choose one.
}
 
float iuf(integer a)
{//union integer to float
    if((a & 0x7F800000) == 0x7F800000)
        return (1 | (a >> 31)) * (float)llList2String(["NaN","Infinity"], !(a & 0x7FFFFF));
    integer b = 0xff & (a >> 23);
    return llPow(2.0, (b | !b) - 150) * (((!!b) << 23) | ((a & 0x7fffff))) * (1 | (a >> 31));
}//will crash if the raw exponent == 0xff; reason for crash deviates from float standard; though a crash is warranted.
The above passed the test in the range where version 1 failed (vicinity of 0x7fffff for mantissa and exponents up to -2). Also, fui(iuf(i))==i produces no mismatch in a random sample of 2M integers (version 1 produced 2 mismatches in a random sample of 1M). Looks promising. I'm not testing it in LSO though - too slow :) --Pedro Oval 06:24, 12 February 2013 (PST)
A distributed computation over the whole range of integers except those representing NaN and Inf (I used 64 scripts in 16 sims computing 64M numbers each) has showed that the fui and iuf functions above work flawlessly for every supported integer. Those representing NaN were not tested for the obvious reason of unavoidable data loss. We have a winner. Thank you so very very much. --Pedro Oval 21:34, 13 February 2013 (PST)
This is kind of scary, I wasn't expecting this version to work. The other version was the one I was expecting to work. Sounds like there were two bugs. I'll compress it back into an optimized form. This makes me worry about Float2Hex working, since it sounds like the string->float typecast doesn't like very small numbers. I wonder if there is a cap on the exponent? I shall have to test. -- Strife (talk|contribs) 11:08, 14 February 2013 (PST)
I've made some experiments now. (float)"something" seems to fail under Mono whenever "something" results in a number with an absolute value less than 0x1p-126, no matter if it's hex or otherwise.
However, (vector)"<something,something,something>" and (rotation)"<something,something,something,something>" both work fine with numbers in that range even in Mono.
Go figure... -- Pedro Oval (talk) 05:41, 19 September 2015 (PDT)


integer fui(float a)//Mono Safe, LSO Safe, Doubles Unsupported, LSLEditor Unsafe
{//union float to integer
    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
        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
        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 /= llPow(2.0, c)))))) << 23 ) | 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)) << 31;//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 0x7FFFFFFF;//NaN time! We have no way to tell NaN's apart so lets just choose one.
}
 
float iuf(integer a)
{//union integer to float
    if((a & 0x7F800000) == 0x7F800000)
        return (1 | (a >> 31)) * (float)llList2String(["NaN","Infinity"], !(a & 0x7FFFFF));
    return llPow(2.0, (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 warranted.


Lots to comment on, and MediaWiki's standard for follow-up responses is quite inconvenient for this purpose. I hope you don't mind if I post this reply without indentation.

First, I'm pretty sure that the failure of the previous function must be due to a bug somewhere, not to the algorithm, given that the failure pattern seems very clear and consistent. I haven't analyzed it so I can't tell more for now.

Second, your sentence "I wonder if there is a cap on the exponent?" suggests to me that you have missed my last reply in the iuf bug discussion.

Third, the "compressed" versions work, though I haven't tested the whole range, just a random sample of 10M integers.

Fourth, the NaN test is flawed. if((a)) is equivalent to if(!(a==0)) and is true for NaNs.

Fifth, the usual NaN returned by gcc is 0x7FC00000, so I suggest to use that for possible interoperability. It has the "quiet" bit set and nothing else.

Sixth, I see quite some room for memory optimizations in Mono for both functions, while minimally affecting performance if at all. Changes include:

  • Changing division to multiplication where possible.
  • Changing <= to < and >= to > where possible. a <= b is equivalent to !(a > b) both in size and in behavior even with NaNs (sad, I know).
  • Changing subtraction to addition of opposite (a-b to a+-b).
  • Changing -constant to its hex representation.
  • Changing 2.0 to 2
  • Changing << constant to * constant (>> and / take the same bytes so no gain there, plus there are sign issues).
  • Changing (a & constant) == constant to !(~a & constant).
  • Using localint^localint instead of 0.
  • Change the logic of Infinity and NaN in iuf, to get rid of the list, using that (float)"Inf" yields infinity and that parsing stops at the first invalid character without invalidating the previous results.
  • Changing (string)-0.0 to "-0.000000" (this one is the only one I don't have reliable experimental results to back, but I believe it will save memory; it almost surely will perform better by not needing to build that string at run time).

Here are Mono-memory-optimized versions taking into account all of these:

integer fui(float a)//Mono Safe, ????LSO Safe???? (untested), Doubles Unsupported, LSLEditor Unsafe
{//union float to integer
    if(a){//is it nonzero?
        integer b = (a < 0) * 0x80000000;//the sign, but later this variable is reused to store the shift
        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 > 1e-45)//It should at this point, except if it's NaN
        {
            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 *= llPow(2, -c)))))) * 0x800000 ) | b);
        }//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.
    }
    return ((string)a == "-0.000000") * 0x80000000;//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.

float iuf(integer a)
{//union integer to float
    if(!(~a & 0x7F800000))
        return (float)llGetSubString("-nan-inf", 4 * !(a & 0x7FFFFF) + (a > (a^a)), 7);
    return llPow(2, (a | !a) + 0xffffff6a) * (((!!(a = (0xff & (a >> 23)))) * 0x800000) | ((a & 0x7fffff))) * (1 | (a >> 31));
}

Further possible optimizations include replacing other divisions with multiplications where possible (llLog(a) / 0.693... might be a candidate, but it's at the core of the algorithm so I didn't dare) and replacing <= with < in the (3 <= ...) if possible. If it's equivalent to (2 < ...) then the latter is preferable. Also, although I've not touched them, source code wise, the floats used have an unnecessary precision; using respectively 2.3509887e-38, 1e-45, 3.4028235e+38, and 0.6931472, the results are the same. You can check that doing: float f1 = 2.3509887016445750159374730744445e-38; float f2 = 2.3509887e-38; if (f1 == f2) llOwnerSay("yes"); outputs yes, and similarly for the rest. Doubles are not supported after all.

For the sake of verifiability, here's my simpler test program (not the distributed one, although it's along the same lines):

string tohex(integer i)
{
    string hex = "0123456789ABCDEF";
    string result;
    integer iter = 9;
    while (--iter)
    {
        result = llGetSubString(hex, i & 0xF, i & 0xF) + result;
        i = i >> 4;
    }
    return result;
}

default
{
    state_entry()
    {
        if (fui(iuf(0x7F800000)) ^ 0x7F800000) llOwnerSay("Fail at +inf");
        if (fui(iuf(0xFF800000)) ^ 0xFF800000) llOwnerSay("Fail at -inf");
        if (fui(iuf(0x7FC00000)) ^ 0x7FC00000) llOwnerSay("Fail at NaN1");
        if (fui(iuf(0xFFC00000)) ^ 0x7FC00000) llOwnerSay("Fail at NaN2");
        if (fui(iuf(0x7FC0FFFF)) ^ 0x7FC00000) llOwnerSay("Fail at NaN3");
        if (fui(iuf(0xFFC00001)) ^ 0x7FC00000) llOwnerSay("Fail at NaN4");
        if (fui(iuf(0x00000000)) ^ 0x00000000) llOwnerSay("Fail at 0.0");
        if (fui(iuf(0x80000000)) ^ 0x80000000) llOwnerSay("Fail at -0.0");
        if (fui(iuf(0x00000001)) ^ 0x00000001) llOwnerSay("Fail at 1e-45");
        if (fui(iuf(0x80000001)) ^ 0x80000001) llOwnerSay("Fail at -1e-45");
        if (fui(iuf(0xFF7FFFFF)) ^ 0xFF7FFFFF) llOwnerSay("Fail at ~ -1e38");
        integer a;
        integer iter;
        while (++iter)
        {
            integer rand = (integer)llFrand(65536) + 65536 * (integer)llFrand(65536);
            if (!(iter & 0xFFFF)) llSetText((string)iter, <1,1,1>, 1);

            if (fui(iuf(rand)) != rand) // we let it run for NaNs too to detect crashes
            {
                if (~rand & 0x7F800000)
                {
                    llSetText("Fail at rand = 0x" + tohex(rand) + ", fui(iuf(rand)) = 0x" + tohex(fui(iuf(rand))), <1,1,1>,1);
                    return;
                }
            }
        }
        llSetText("Done", <1,1,1>, 1);
    }
}

I simply let it run while desired.

--Pedro Oval 11:02, 15 February 2013 (PST)

I hadn't seen your previous comment, thank you for pointing it out. I'll try to patch Float2Hex so it doesn't use exponents less than -127 (it use to work on Mono ~_~).
You are correct, the extra precision is unnecessary for single precision. I use these values because Float2Hex uses them, and I've written versions of Float2Hex that support higher precisions. I wrote both originally in ESL (LSL fed through a C Pre-processor) with extensive use of macros.
I tend to optimize for LSO bytecode and stack size (I know it's insane but old habits die hard). Which is why I wasn't using llPow. I'm not surprised there is a bytecode savings between 2 and 2.0 for Mono but is it really faster? I would have though the compiler would have put in the implicit typecast there... unless LL got smart... hmmm. Anyway we are well beyond optimizing for LSO.
You could try flipping the divide in the log2 calculation, I'm not sure if the error recovery is good enough. My recollection from my initial testing was that using a multiply instead of the divide widened the range of the errors.
I never wanted to committing to inlining "-0.000000" in fear they might change the typecast on me. We won that fight a few years ago so it should be ok.
Quick question, what does the following do? Trying to figure out how screwed I am. I am hoping to avoid having Float2Hex revert to base 10 for |x| < 0x1p-127 (I don't even know of a good way to do that x_x).
default {
    state_entry() {
        llOwnerSay((string)(0.0 != (float)"0x0.4p-127"));
    }
}
Unless testing proves otherwise, we don't want to swap in a multiplication for a shift, at the CPU level shift should be faster. Swapping a subtract out for an add-negation similarly won't give you much as negation would be an extra instruction (unless the negation is being done on a constant than it's no extra).
1e-45 makes me uneasy. I know it works but it just looks wrong to me. I am cool with excessive precision.
I've made a few changes. I couldn't help myself. I used the dreaded negative indexing on llGetSubString, with wraparound. -- Strife (talk|contribs) 22:16, 16 February 2013 (PST)
integer fui(float a)//Mono Safe, LSO Safe, Doubles Unsupported, LSLEditor Unsafe
{//union float to integer
    if(a){//is it nonzero?
        integer b = (a < 0) * 0x80000000;//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 ((string)a == "-0.000000") * 0x80000000;//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));
}


The version with multiply turned out to work fine in a sample of ~600M random numbers, so yay! On the bad news side, the test program you asked me to run returns 0. As I said, 1p-127 returns 0 already, so no surprise there, but I tested this:
default
{
    state_entry()
    {
        llOwnerSay((string)(0.0 != (float)"0x0.ffffffp-126"));
        llOwnerSay((string)(0.0 != (float)"0x1.0p-126"));
    }
}
and got 0 and 1. So I'm afraid hex notation refuses to produce any denormal at all. A strategy you can maybe use for denormals is to treat them as a special case multiplying them by a power of two, so they come into a tractable range. But I haven't checked the source so I may be off here. (Oops, got your point now - there may be a way using part of the strategy I mention, I'll think about something)
As for optimizations, I like your "risky" rewriting of llGetSubString, and of course it works. I know mults are slower than shifts (if that's what the code is compiled to in the end, which is questionable), but the difference, just maybe ten cycles or so, is really unnoticeable when there's a whole burden of task switching and other stuff going on. For that reason I tend to always optimize for memory. In the above case, it will matter little if the function takes 30000 or 30010 cycles to execute, just as an example to make my point (I'm sure the actual numbers will be different). Same with minus sign: the extra CPU burden will be insignificant (1 cycle probably). I've tried to test these differences but I never got conclusive results because noise level exceeds signal level by far, which just proves how insignificant the difference is. I tend to run out of space in scripts so I mostly optimize for memory. While I get to 60K+ code size often, I have yet to run into an application where shoving cycles that way gains anything noticeable. Also, I started to test this for Omei because she intended to use it in a memory-tight application. The space savings can make a difference.
The int-to-float implicit conversion seems to add an explicit cast, as an int constant is 6 bytes, a float constant is 10, and an integer implicitly or explicitly cast to float takes 7. By the way, -~-x is a saving vs. 1-x and vs. 1+-x (1 byte per unary sign vs. 6 bytes the integer constant, 8 the subtraction and 4 the addition). I have Mono size measurements here: User:Pedro Oval/Mono code memory usage.
As for the 1e-45, OK, though I like it because it also reflects the loss of precision that happens in denormals. But it's my opinion that using the whole expansion in the comparison to detect a NaN is overkill, as any number between 1e-45 and 2.35e-38 will do. For example, 1e-40 is equally valid and doesn't clobber the source so much. --Pedro Oval 19:36, 17 February 2013 (PST)
Alas, it's worse than I thought. 1.17e-38 (a denormal) works fine, but (float)"1.17e-38" returns 0. Not sure when did glibc stop supporting denormals. --Pedro Oval 19:59, 17 February 2013 (PST)
I've put -~- back. I can't thank you enough for all your testing. I totally agree, optimizing for size is the main concern. I didn't remember this about logarithms that logab * logba = 1. -- Strife (talk|contribs) 21:38, 17 February 2013 (PST)
Crap. Both Float2Sci and Float2Hex are screwed. Without having access to the source for the Mono compiler we can't be sure it's glibc or Mono, though we can confirm it's glibc that's bugged by checking how it works with LSO. I may just have to manifest at an upcoming Simulator/Server_Beta User Group meeting and complain. should open up a ticket on jira first. hmmm. -- Strife (talk|contribs)
Duh, good point. It's a Mono-only quirk. (float)"0x1p-127", (float)"0x1p-149", and (float)"1e-45" are all nonzero in LSO, and the latter two are equal to 1e-45 as expected. About the latest change, I fail to see the gain in moving the sign of c to the -~-llFloor line, as that forces a subtraction later which eats 4 extra bytes. Could it not remain as integer c = ~-llFloor..., then ...llPow(2, -c))))) + c)? --Pedro Oval 20:23, 18 February 2013 (PST)
Of course it can but why should it make a difference as we have just moved the negation further down. Am I missing something? Please tell me that subtraction isn't more expensive than addition. -- Strife (talk|contribs) 21:47, 18 February 2013 (PST)
Sorry to bring bad news but yes: addition of two local integers takes 4 bytes, as does multiplication. But subtraction takes 8, as do division, mod and shift. "!" takes 3 bytes, while "~" and "-" take 1 each. Take a look at the table I linked to. You'll want to cry. As for the several variants you enumerate below, regrettably, float to integer cast takes 5 bytes, which is more than leaving it as <= instead of < (3 extra bytes). I think function calls need at least as many bytes as their name, so I wouldn't go that route. Not 100% sure there though. Converting x-y to x+-y saves 3 bytes. --Pedro Oval 14:31, 19 February 2013 (PST)
Oh yeah, ((integer)x - (3 <= x)) is equivalent to...
  • (integer)x - (2 < (integer)(x))
  • (integer)x - (((integer)(x)) == 3)
  • (integer)x + (((integer)(x)) / -3)
  • (integer)x - !(3 & ~(integer)(x))
  • (integer)x - (2 < llFloor(x))
  • (integer)x - ((llFloor(x)) == 3)
  • (integer)x + ((llFloor(x)) / -3)
  • (integer)x - !(3 & ~llFloor(x))
the value of x will always be in the range [0.5, 4). Truncating or rounding it down to an integer result in it being in the range [0, 3], we want to further truncate this range to [0, 2]. -- Strife (talk|contribs) 22:33, 18 February 2013 (PST)
  • (integer)x + (integer)(x * -0.3333333432674407958984375)
  • (integer)x - (2.9999997615814208984375 < x))
Another reason for the excessive precision, I test a lot of code in LSLEditor, which uses doubles. If I want things to work I have to keep my constants in double precision land. I calculate most of them with the windows calculator and then just copy and paste. That way when I paste them back into the windows calculator I can work the math backwards and figure out where the constants came from. -- Strife (talk|contribs) 23:43, 18 February 2013 (PST)

too many (, or not enough )

return (0x3FF & (integer)(a * (0x800 >> b))) | (((c + (b = ((integer)a - (3 <= (a *= (0.0000152587890625 * (0x40000000 >> c)))))) << 10 ) | b);

count("(") != count(")") Cron Stardust 07:04, 27 June 2013 (PDT)

I think that's a left over from a last minute refactoring. Fixed. -- Strife (talk|contribs) 20:36, 27 June 2013 (PDT)
Thanks! :D Cron Stardust 20:55, 27 June 2013 (PDT)

Indeterminates

There's one more value that can be encoded and read back: indeterminate. Indeterminate values have a representation of FFC00000, see http://babbage.cs.qc.edu/courses/cs341/IEEE-754references.html#kevin_chart. They can be read through llList2CSV and they show as -nan.

default
{
    state_entry()
    {
        float a=(float)"nan";
        llOwnerSay(llList2CSV(["(float)\"nan\"", a]));
        a=-a;
        llOwnerSay(llList2CSV(["-a", a]));
        a=(float)"inf"*0;
        llOwnerSay(llList2CSV(["inf*0",a]));
        a=llAcos(2);
        llOwnerSay(llList2CSV(["acos(2)",a]));
    }
}
/*outputs:
[08:05:05] Object: (float)"nan", nan
[08:05:05] Object: -a, -nan
[08:05:05] Object: inf*0, -nan
[08:05:05] Object: acos(2), nan
*/

(float)"-nan" does not work :( --Sei Lisa (talk) 08:06, 6 September 2015 (PDT)

I think I knew (float)"-nan" was borked, can't really do anything about it. I guess I'll multiple +/-inf by zero or one for the -nan generation. -- Strife (talk|contribs) 17:05, 6 September 2015 (PDT)
It should be better now. Haven't compiled it. Probably should. No time now. Half-precision won't work right, it needs some TLC. -- Strife (talk|contribs) 17:45, 6 September 2015 (PDT)
Thanks a lot! fui is working perfectly, but iuf can't produce nan, only -nan. If it helps, I've found that while (float)"-nan" doesn't work, -(float)"nan" and -1*(float)"nan" do.--Sei Lisa (talk) 18:34, 6 September 2015 (PDT)
Correction. (-1)*(float)"nan" doesn't produce -nan. The parentheses were important.--Sei Lisa (talk) 08:21, 9 September 2015 (PDT)
That makes some perverse sense. I'll log in sometime this weekend and figure out a way to do this (hopefully without using a branch). -- Strife (talk|contribs) 15:26, 12 September 2015 (PDT)


FUI2HexFloat

This fuction can t be compiled . Error at the line if(e ^ 127) e is a variable declared later

Miranda Umino (talk) 11:28, 14 September 2015 (PDT)

it should compile now but it needs testing. --Strife (talk|contribs) 13:39, 14 September 2015 (PDT)