• All submissions to this site are governed by Second Life Project Contribution Agreement. By submitting patches and other information using this site, you acknowledge that you have read, understood, and agreed to those terms.
Issue Details (XML | Word | Printable)

Key: SVC-3328
Type: Bug Bug
Status: Resolved Resolved
Resolution: Fixed
Priority: Major Major
Assignee: Scouse Linden
Reporter: Tackmee Papp
Votes: 5
Watchers: 2
Operations

If you were logged in you would be able to see more operations.
2. Second Life Service - SVC

mono script : bad calculated 100*0.9 ==> 89 !

Created: 27/Oct/08 04:40 AM   Updated: 09/Jul/09 10:44 AM
Return to search
Component/s: Scripts
Affects Version/s: 1.24 Server
Fix Version/s: 1.27 Server

Environment:
Second Life 1.21.6 (99587) Oct 14 2008 17:42:25 (Second Life Release)

209745.8, 289882.5, 27.0 in FYC como located at sim8416.agni.lindenlab.com (8.10.150.229:13002)
Second Life Server 1.24.9.98659

CPU: Intel Core 2 Series Processor (2400 MHz)
Memory: 2046 MB
OS Version: Microsoft Windows Vista Service Pack 1 (Build 6001)
Graphics Card Vendor: NVIDIA Corporation
Graphics Card: GeForce 8800 GTS/PCI/SSE2
OpenGL Version: 2.1.2
Issue Links:
Duplicate
 
Relates
 

Last Triaged: 28/Oct/08 11:09 AM
Linden Lab Issue ID: DEV-22962
Linden Lab Internal Branch: server-1.25


 Description  « Hide
mono script can not calculate below patern.

llSay(0, (string)((integer)(100*0.9)) );

This result is 89 in mono.

>retouch..
Result in LSL2 is 90.



 All   Comments   Change History      Sort Order: Ascending order - Click to sort in descending order
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?

Tackmee Papp added a comment - 27/Oct/08 05:50 AM - edited
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);
llSay(0, (string)int); => result 89

integer int=(integer)(100.0 * 0.9);
llSay(0, (string)int); => result 89

but of course
llSay(0, (string)(100*0.9) ); => result 90.000000


Chalice Yao added a comment - 27/Oct/08 06:13 AM
Okay, definitely broken, and I personally think this should be elevated to 'Major', considering how many financial calculations are made in SL.

Tackmee Papp added a comment - 27/Oct/08 06:21 AM
Thank you for your advice.
I changed this issue priority to 'Major'.

Strife Onizuka added a comment - 27/Oct/08 07:42 AM - edited
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 on the other hand has a fixed precision but that precision can be shifted up and down a much larger range.

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:
(sign?-1:1) * (1 + mantissa / (mantissa_max + 1)) * 2 ^ exponent;

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:
1) Some numbers are impossible to accurately represent as floats.
2) You will accumulate errors when you use floats.
3) Don't use floats unless you need to.
4) Consider using llRound instead of truncation.


Now for the reasons why this will never be fixed:
1) The floating point standard previously mentioned is an industry wide hardware standard (originating in the 1980's). If you want something different you have to implement it yourself in software or fabricate your own CPUs. Both are expensive and beyond the scope of what LL does.
2) It would break existing scripts that (inadvertently) depend upon the rounding quirks.


Tackmee Papp added a comment - 27/Oct/08 08:48 AM
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)
llSay (0, (string) ((integer) (100 * 0.9))); => result 89
llSay (0, (string) (100 * 0.9)); => result 90.000000

It will get confused.
If the phenomenon of internal processing float only, those results should have the same values.

2)
Result in LSL2,
llSay (0, (string) ((integer) (100 * 0.9))); => result 90
so, mono is not compatible to LSL2.


Chalice Yao added a comment - 27/Oct/08 08:57 AM - edited
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.

Tackmee Papp added a comment - 27/Oct/08 08:58 AM - edited
Reopening because LSL2 and Mono give different result.
(I appreciate your comment. )

Moon Metty added a comment - 27/Oct/08 09:15 AM
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
Unfortunately Mono seems to be a little less reliable than LSL, maybe it can be fixed.


Strife Onizuka added a comment - 27/Oct/08 09:30 AM
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).

Strife Onizuka added a comment - 27/Oct/08 09:58 AM
Mono could be using a different CPU opcode than LSO, specifically FMA (Fused Multiply Add) vs FADD.

Prospero Linden added a comment - 04/Nov/08 07:51 AM
A fix for this is in the 1.25 server code (currently on the "Second Life Beta Server" regions of the Preview Grid, aditi).

chemiker kohl added a comment - 11/Nov/08 04:45 PM
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.

Strife Onizuka added a comment - 18/Nov/08 04:06 PM
Prospero, I'm curious what the cause of this was, mind giving us a quick rundown on the cause and the fix?

Hewee Zetkin added a comment - 18/Nov/08 04:47 PM - edited
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.


Viktoria Dovgal added a comment - 18/Nov/08 07:22 PM
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.

Chalice Yao added a comment - 19/Nov/08 01:24 AM
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.


Scouse Linden added a comment - 20/Jan/09 02:48 AM
This bug needs a staged roll out. The actual fix will appear in one of the 1.25 or 1.26 servers.

Periapse Linden added a comment - 02/Feb/09 03:58 PM
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


James Benedek added a comment - 09/Jul/09 10:44 AM
Fixed in roll-out of Second Life Server 1.27.0.126647 on grid agni at Island for Unit Tests.

[10:40] Object: 90
[10:40] Object: 90
[10:41] Object: 90
[10:41] Object: 90
[10:42] Object: 90.000000