User talk:Strife Onizuka/Float Functions
What's the purpose of llFloatCompare?
Hi, I'm a bit confused by llFloatCompare, why include fui, iuf and FloatCompare in a script when you can just do it this way:
<lsl>
float f_1 = 0.00000003; float f_2 = 0.00000002; if(llFabs(f_1) - llFabs(f_2) > 0 ) { llOwnerSay("f_1 > f_2"); return; } if(llFabs(f_1 - f_2) == 0.0) { llOwnerSay("f_1 = f_2"); return; } llOwnerSay("f_1 < f_2");
</lsl>
I have run a test in Second Life, using your FloatCompare function (because none of required functions even compile on OpenSim) and compared it to my method and the results are the same, even my simple method succeeds where your method fails. I used the following script:
<lsl> // [ bla bla bla ] // I've spared you the 40 lines of code and functions that your FloatCompare method requires.
default {
state_entry() { float f_1 = 0.999993; float f_2 = 0.999992; if(llFabs(f_1) - llFabs(f_2) > 0 ) { llOwnerSay("Kira: f_1 > f_2"); jump strife; } if(llFabs(f_1 - f_2) == 0.0) { llOwnerSay("Kira: f_1 = f_2"); jump strife; } llOwnerSay("Kira: f_1 < f_2");
@strife;
llOwnerSay("Strife: " + (string)FloatCompare(f_1, f_2, 1)); }
} </lsl>
Test 1, Your method fails.
For <lsl> float f_1 = 0.9999993; float f_2 = 0.9999992; </lsl>
Output
Object: Kira: f_1 > f_2 Object: Strife: 0
They are not equal... What is this?
Test 2, Your method fails.
For: <lsl> float f_1 = 0.9999003; float f_2 = 0.9999002; </lsl>
Output
Object: Kira: f_1 > f_2 Object: Strife: 0
Still equal? WTH?
Test 3, We both succeed.
For: <lsl> float f_1 = 0.999993; float f_2 = 0.999992; </lsl>
Output
Object: Kira: f_1 > f_2 Object: Strife: 1
Test 4, We both fail.
<lsl> float f_1 = 0.000000000000000000000000000000000000000000002; float f_2 = 0.000000000000000000000000000000000000000000001; </lsl>
Output
Object: Kira: f_1 = f_2 Object: Strife: 0
Here are a few funny ones:
Test 5, My method works, your method just crashes with Math Error
<lsl> float f_1 = 3.402E+38; float f_2 = 3.403E+38; </lsl>
Output
Object: Kira: f_1 < f_2 Object: Object [script:New Script] Script run-time error Object: Math Error
This one too:
Test 6, My method works, your method just crashes with Math Error
<lsl> float f_1 = 3.403E+38; float f_2 = 3.402E+38; </lsl>
Output
Object: Kira: f_1 > f_2 Object: Object [script:New Script] Script run-time error Object: Math Error
Don't get me wrong, you provide no documentation on what those functions are doing (what they mean) so I don't know if I used them correctly...
- Could you please clarify what they are supposed to do?
- Why include this chunk of obfuscated garbage with magic numbers:
<lsl> integer fui(float a)//Mono Safe, LSO Safe, Doubles Unsupported, LSLEditor Unsafe {//union float to integer
if((a)){//is it non zero? integer b = (a < 0) << 31;//the sign, but later this variable is reused to store the shift if((a = llFabs(a)) < 2.3509887016445750159374730744445e-38)//Denormalized range check & last stirde of normalized range return b | (integer)(a / 1.4012984643248170709237295832899e-45);//the math overlaps; saves cpu time. integer c = llFloor((llLog(a) / 0.69314718055994530941723212145818));//extremes will error towards extremes. following yuch corrects it. return (0x7FFFFF & (integer)(a * (0x1000000 >> b))) | (((c + 126 + (b = ((integer)a - (3 <= (a /= (float)("0x1p"+(string)(c -= ((c >> 31) | 1)))))))) << 23 ) | b); }//for grins, detect the sign on zero. it's not pretty but it works. the previous requires alot of unwinding to understand it. return ((string)a == (string)(-0.0)) << 31;
}
float iuf(integer a) {//union integer to float
return ((float)("0x1p"+(string)((a | !a) - 150))) * ((!!(a = (0xff & (a >> 23))) << 23) | ((a & 0x7fffff))) * (1 | (a >> 31));
}//will crash if the raw exponent == 0xff; reason for crash deviates from float standard; though a crash is warented.
integer FloatCompare(float a, float b, integer c) {//compare floats and allow for a margin of error, requires fui().
if(a - b)//(c) Strife Onizuka 2006 {//they are not equal //First we convert the floats to integer form, as they would be in memory; integer a_i = fui(a); integer b_i = fui(b); integer a_e = (a_i >> 23) & 0xff; integer b_e = (b_i >> 23) & 0xff; if(!(a_e || b_e) || //to disable the +/- roll under support put a // just before the ! ((a_i & 0x80000000) == (b_i & 0x80000000)))//sign match check {//start by getting and testing the difference, this is what limits c integer diff = a_e - b_e;//ugly is fast, basicly, it gets the mantissa, sets the sign on the mantisa, if(diff >= -1 || diff <= 1)//shifts it depending on exponent, finaly executes the test. if(llAbs(((((a_i & 0x7FFFFF) | (!!a_e << 23)) * ((a_i >> 31) | 1)) >> !~-diff) - ((((b_i & 0x7FFFFF) | (!!b_e << 23)) * ((b_i >> 31) | 1)) >> !~diff)) <= c) jump out; } return (a > b) - (a < b); } @out; return 0;
} </lsl>
in every script when you can use the method above?
Your scripts seem to either give wrong answers, match mine or, in your case, they just crash...
Kira Komarov 06:12, 23 May 2012 (PDT)
- FloatCompare is designed to tell you if two floats are close together. Closeness is not specified by some hardcoded constant but by the magnitude of the numbers. To do this we get the memory representation of the floats (as integers, it's what fui does). Then compare the mantissas and exponents. The integer parameter specifies the max difference between them.
- As to magic numbers. Some are shared with Float2Hex and that has pretty good documentation. GTG -- Strife (talk|contribs) 11:50, 23 May 2012 (PDT)
- Right. So where is "the magnitude of numbers" specified? To be more precise, if "closeness" is not specified by some hardcoded constant then how is it specified? Telling me what it is not specified by does not help because I can tell you what it is not specified by as well... For example, I am quite sure it's not specified by lederhosen... As it stands, aside from the crashes, it does not seem to do the job. Also, if you use integers to specify (this word has been abused so much, it should go on strike) the maximum difference (is this the "closeness" you mentioned?) between floats, then what is the point of it? I mean is there some mathematics involved or is it sort of an empirical measurement where some comparisons fail, some pass and some others crash?
- I may take a look at Float2Hex later since that's another mystery but you suggested these functions for a project of mine (when I tried to explain determinism to you) and I guess it was a good choice not to use them. For example, you could take my 9-liner code and introduce another float which can give you the distance between those floats precisely (through elementary inequalities). I am not sure I follow your reasoning. In fact is there any reasoning at all? Are there any mathematical definitions that would explain what "closeness" means in your case?
- Your girlfriend here tosses your functions to Linden as an example. However your functions fail the most basic tests (again, leaving aside the funny crashes) and are not explained at all. How do you expect Linden to take action on something that neither you, or her seem to fully comprehend let alone explain in detail?
- She even cites your code with "//-- formating functions, please ignore", but how can you ignore that if it doesn't work properly. I don't even have to follow your obsessions on bit twiddling, I can just run the tests above and get undefined behavior (and that's putting it boldly because you just dump the function with no explanation, so I guess I don't know what the defined behavior is in the first place to be able to claim that it's undefined). Did you take this code from somewhere? Perhaps it had a point there...
- Kira Komarov
- I know why fui was crashing. 3.403E+38; is getting rounded up to
inf
. 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).
- I wrote the function in the course of examining floating point arithmetic drift. That is to say the steady corruption of the lowest bits due to arithmetic operations. The problem is corruption manifests itself in the values of the bits in the mantissa of the float, it can even result in the exponent being increased.
- I feel I need to explain how floats work so lets take a moment to go over that. However it's easier for me to provide code that replicates how it works than to put how it works into words.
- A float is made of three parts. This is how it is stored in memory. All operators take it in this for, perform their operation and return a value in this form. A float is just a collection of bits that have meaning given a specific context.
- sign - 1 bit
- exponent - 8 bits (unsigned)
- mantissa - 23 bits (unsigned)
- The value of a float is calculated as follows:
<lsl>float value(integer sign, integer exponent, integer mantissa){
//make the sign 1 or -1, zero maps to 1 sign = 1 | -!!sign; //For the sake of simplicity we will ignore NaN and Infinity values. //normalized range if(exponent) return sign * (0x800000 + mantissa) * llPow(2.0, exponent - 150);
//denormalized range return sign * mantissa * llPow(2.0, -149);
}</lsl>
- Now LSL is not C, in C you can take a float, do some hand waving and presto you can read the float value as if it were an integer value. That is to say, given the memory location of the float, read that location as if it were an integer. Since LSL does not let you do this (though there is nothing stopping you from doing this in LSO), I wrote a set of functions to approximate this. This is what FUI and IUF do (their only flaw is not properly handling
nan
andinfinity
). I wrote these functions of an LSL based LSO interpreter that I never finished. They are an evolutionary dead end a solution without a problem to solve.
- The problem is that as you perform certain arithmetic operations you get drift. The lowest bits of the mantissa do not match up with the math. That is to say, the way we as humans evaluate the operations is slightly different than the way the computer does. Take for example (1.0 / 10) * 10. As humans we would say that it is equal to 1. The computer on the other hand will say it's not equal to 1. This is because the result of (1.0 / 10) is an approximation, the precise value would a different way of storing the value.
- What this function does is let you compare the mantissa of the two floats. In it's most basic form it's
((mantissa_a - mantissa_b) > c)
(this simplification doesn't take into consideration that the exponents won't match, or that the sign of the return is based on the larger value).
- Lets look at the following function (I just wrote this and haven't had time to test it):
<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).
- 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
union{ float f; int i; }
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.
- 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. -- Strife (talk|contribs) 23:14, 24 May 2012 (PDT)
- 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.
- 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...
- 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:
- 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 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...
- 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.
- 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...
- 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.
- 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...
- I still cannot understand why you would create a a Jira for that when, in essence, it has nothing to do with you...
- Kira Komarov 12:07, 25 May 2012 (PDT)
P.S.
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.
... Don't feed it to me though, thanks. I can handle it myself.
Kira Komarov 12:15, 25 May 2012 (PDT)
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: <lsl> // 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); </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
—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)
<lsl> 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) {//union integer to float
return ((float)("0x1p"+(string)((a | !a) - 25))) * ((!!(a = (0x1f & (a >> 10))) << 10) | ((a & 0x7ff))) * (1 | (a >> 15));
} </lsl>