|
|
|
[
Permlink
| « Hide
]
Chalice Yao added a comment - 27/Oct/08 05:23 AM
Out of curiosity, do you get the same result if you use 100.0 * 0.9?
I tried some variations.
llSay(0, (string)((integer)(100 * 0.9)) ); => result 89 llSay(0, (string)((integer)(100.0 * 0.9)) ); => result 89 integer int=(integer)(100 * 0.9); integer int=(integer)(100.0 * 0.9); but of course Okay, definitely broken, and I personally think this should be elevated to 'Major', considering how many financial calculations are made in SL.
Thank you for your advice.
I changed this issue priority to 'Major'. This is not an LSL issue or a Mono issue. This is a known design flaw of the floating point standard. The design reasoning was complex and remains very technical; to understand the problem you need to know how floats and integers work.
An integer is just a number, but limited to a fixed range. To increase the range of an integer you must increase the number of bits, regardless of the precision you want or is useful. A float has three components, the sign, mantissa and exponent. The sign is just a single bit but the mantissa and exponent are integer values: the value of the float value is: As you can see, the exponent is on base two and not base ten; with the mantissa calculation it falls in the interval [1,2). How does this apply to this situation? With a float it is impossible to accurately represent 0.9, the best you can get is 0.899999999, when you multiply that by 100 you get 89.9999999. When you did the integer truncation, you got 89. Lessons to learn from this: Now for the reasons why this will never be fixed: Thanks for your comment.
I had used Fortran77 a few years ago. Then,I know your indication. Float calculation contains few numerical difference. However, I think, this problem in mono is the next two points. 1) It will get confused. 2) 1) The floating point rounding issues coming from the standard don't affect simple conversions like (integer)100 * 0.9
2) As shown above, it is a Mono problem, since LSL2 rounds it correctly. Thus, it does break all previous scripts that use such conversion when converting them to Mono. Reopening. Reopening because LSL2 and Mono give different result.
(I appreciate your comment. ) Yes, Mono doesn't behave the same as LSL in these cases.
// SVC-3328
default
{
state_entry()
{
llSay(0, (string)((integer)(0.7 * 10.0))); // 6 in Mono, 7 in LSL
llSay(0, (string)((integer)(10.0 * 0.7))); // 6 in Mono, 7 in LSL
float var_float = 7.0 / 10.0;
llSay(0, (string)((integer)(var_float * 10.0))); // 6 in Mono, 7 in LSL
llSay(0, (string)((integer)((7.0 / 10.0) * 10.0))); // always 7
llSay(0, (string)((integer)(7.0 / 10.0 * 10.0))); // always 7
var_float *= 10.0;
llSay(0, (string)((integer)(var_float))); // always 7
var_float = (7.0 / 10.0) * 10.0;
llSay(0, (string)((integer)(var_float))); // always 7
}
}
Never trust a float to integer conversion hmm, I'm sorry for prematurely resolved it. Mono may be setup to use a different float rounding mode than LSO (there are 4 supported by the standard).
Mono could be using a different CPU opcode than LSO, specifically FMA (Fused Multiply Add) vs FADD.
A fix for this is in the 1.25 server code (currently on the "Second Life Beta Server" regions of the Preview Grid, aditi).
The usual way of handling this is instead of (integer)(fval) use a function LLnint(fval) which returns the integer value nearest to fval. LSL has LLceil(fval) and LLfloor(fval), but not LLnint.
Prospero, I'm curious what the cause of this was, mind giving us a quick rundown on the cause and the fix?
I'm also curious about the cause of this. From what I can tell, the MONO behavior is the correct one. Decimal 0.9 is binary 0.1_1100_ (where the underscores delimit the repeating sequence), or 0.1|11001100110011001100110|01100..., with bars (|) inserted to show the part that will fit in the 23-bit mantissa (the first 1 being implicit due to floating point normalization). As the places after the mantissa are clearly less than one half, the value should round DOWN. That means the result of the multiplication 10.0*0.9 should be less than 9.0, and should correctly truncate to 8 when cast to an integer.
The same is true of 10.0*0.7. 0.7 is binary 0.1_0110_ or 0.1|01100110011001100110011|00110... which will clearly round DOWN, making the result of 10.0*0.7 slightly LESS than 7.0 and the result of truncation to an integer should be 6. The LSL2 behavior seems to be consistent with C casting and assignment from float to to int, and LSL has been following that for years. It's a little late to be talking about using a different method for LSL casts.
I agree with Viktoria.
That, and people expect simple calculations that an elementary schooler could solve to give the correct result. They don't care how things go internally in the bit values, they don't care how they would display without a modern implementation that handles corrections to the dilemma of internal float representations; they expect such simple things to give the correct result defined by the rules of mathematics, unless you go into the really long/high floats where such things get unfeasable to correct. This bug needs a staged roll out. The actual fix will appear in one of the 1.25 or 1.26 servers.
Staged roll out of Server first and then compiler.
The server fix is out on agni now. Next is to check in the compiler changes for a later deploy Fixed in roll-out of Second Life Server 1.27.0.126647 on grid agni at Island for Unit Tests.
[10:40] Object: 90 |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||