Page 2 of 3
X# round() behaviour
Posted: Sat Apr 20, 2019 11:33 am
by Chris
Guys, the problem is .Net's Math.Round() method (which the runtime's Round() function is calling) which is not giving consistent results itself. See the results of this method in the following tests, they do not make any sense:
Code: Select all
FUNCTION Start() AS VOID
LOCAL IMPLIED afw := MidpointRounding.AwayFromZero
? Math.Round(65.305,2 , afw) // 65,31
? Math.Round(65.315,2 , afw) // 65,32
? Math.Round(65.325,2 , afw) // 65,33
? Math.Round(65.335,2 , afw) // 65,33
? Math.Round(65.345,2 , afw) // 65,35
? Math.Round(65.355,2 , afw) // 65,36
? Math.Round(65.365,2 , afw) // 65,36
? Math.Round(65.375,2 , afw) // 65,38
? Math.Round(65.385,2 , afw) // 65,39
I think we will most probably need to ditch Math.Round() and use a custom solution instead as Karl-Heinz suggested.
X# round() behaviour
Posted: Sat Apr 20, 2019 12:09 pm
by lumberjack
Hi Guys,
Chris wrote:Guys, the problem is .Net's Math.Round() method (which the runtime's Round() function is calling) which is not giving consistent results itself. See the results of this method in the following tests, they do not make any sense:
I think we will most probably need to ditch Math.Round() and use a custom solution instead as Karl-Heinz suggested.
Sorry, never really gave attention to this, but realized now the problem. Math.Round() uses what is called Bankers rounding. See this link to force a ToEven or AwayFromZero rounding. It is actually relatively easy to fix.
Rounding overloads
X# round() behaviour
Posted: Sat Apr 20, 2019 12:33 pm
by lumberjack
Chris,
Are the runtime forcing a cast to decimal before the rounding?
X# round() behaviour
Posted: Sat Apr 20, 2019 1:32 pm
by FFF
Johan,
i doubt, bankers rounding explains why the result depends of the integer part...
Try
FUNCTION Start( ) AS VOID
? Round ( 1.475 , 2 )
? Round ( 2.475 , 2 )
? Round ( 3.475 , 2 )
? Round ( 4.475 , 2 )
? Round ( 5.475 , 2 )
? Round ( 6.475 , 2 )
? Round ( 7.475 , 2 )
? Round ( 8.475 , 2 )
? Round ( 9.475 , 2 )
? Round ( 10.475 , 2 )
? Round ( 11.475 , 2 )
RETURN
ALL of them return .48 - except the 4.475 which gives .47
Karl
X# round() behaviour
Posted: Sat Apr 20, 2019 2:27 pm
by Chris
Johan, please see my sample that demonstrates the problem, it does not use bankers rounding.
X# round() behaviour
Posted: Sat Apr 20, 2019 2:31 pm
by Jamal
Karl,
I know it is not a total fix for the Round() function, that's why I called it RoundTest() to show that FLOAT conversion is needed.
FWIW, I never used a negative decimal with the Round function; I guess I am not a banker.
Jamal
X# round() behaviour
Posted: Sat Apr 20, 2019 3:36 pm
by lumberjack
Hi Chris,
Chris wrote:Johan, please see my sample that demonstrates the problem, it does not use bankers rounding.
Ok, investigated a bit further, see this comparison between Round and Decimal.Round on the following example:
Code: Select all
FUNCTION Start() AS VOID
LOCAL u := 5.0 AS USUAL
LOCAL y AS USUAL
SetFloatDelta(10)
SetDecimal(9)
FOR LOCAL x := 0 TO 9
FOR LOCAL i := 1 UPTO 9
y := x + ( u * 10^(-i))
? y, i - 1, PadL(Round(y, i - 1), 15), Decimal.Round((Decimal)(REAL8)y, i - 1, MidpointRounding.AwayFromZero)
NEXT
NEXT
RETURN
0.500000000 0 1 1.000000000
0.050000000 1 0.1 0.100000000
0.005000000 2 0.01 0.010000000
0.000500000 3 0.001 0.001000000
0.000050000 4 0.0001 0.000100000
0.000005000 5 0.00000 0.000010000 // Note here...
0.000000500 6 0.000001 0.000001000
0.000000050 7 0.0000001 0.000000100
0.000000005 8 0.00000001 0.000000010
1.500000000 0 2 2.000000000
1.050000000 1 1.1 1.100000000
1.005000000 2 1.00 1.010000000 // Note here...
1.000500000 3 1.001 1.001000000
1.000050000 4 1.0001 1.000100000
1.000005000 5 1.00001 1.000010000
1.000000500 6 1.000001 1.000001000
1.000000050 7 1.0000001 1.000000100
1.000000005 8 1.00000001 1.000000010
2.500000000 0 3 3.000000000
2.050000000 1 2.1 2.100000000
2.005000000 2 2.01 2.010000000
2.000500000 3 2.001 2.001000000
2.000050000 4 2.0001 2.000100000
2.000005000 5 2.00000 2.000010000 // Note here...
2.000000500 6 2.000001 2.000001000
2.000000050 7 2.0000001 2.000000100
2.000000005 8 2.00000001 2.000000010
3.500000000 0 4 4.000000000
3.050000000 1 3.1 3.100000000
3.005000000 2 3.01 3.010000000
3.000500000 3 3.001 3.001000000
3.000050000 4 3.0001 3.000100000
3.000005000 5 3.00001 3.000010000
3.000000500 6 3.000001 3.000001000
3.000000050 7 3.0000001 3.000000100
3.000000005 8 3.00000001 3.000000010
4.500000000 0 5 5.000000000
4.050000000 1 4.1 4.100000000
4.005000000 2 4.01 4.010000000
4.000500000 3 4.000 4.001000000 // Note here...
4.000050000 4 4.0001 4.000100000
4.000005000 5 4.00001 4.000010000
4.000000500 6 4.000000 4.000001000 // Note here...
4.000000050 7 4.0000001 4.000000100
4.000000005 8 4.00000001 4.000000010
5.500000000 0 6 6.000000000
5.050000000 1 5.1 5.100000000
5.005000000 2 5.01 5.010000000
5.000500000 3 5.001 5.001000000
5.000050000 4 5.0001 5.000100000
5.000005000 5 5.00001 5.000010000
5.000000500 6 5.000001 5.000001000
5.000000050 7 5.0000001 5.000000100
5.000000005 8 5.00000001 5.000000010
6.500000000 0 7 7.000000000
6.050000000 1 6.1 6.100000000
6.005000000 2 6.01 6.010000000
6.000500000 3 6.001 6.001000000
6.000050000 4 6.0001 6.000100000
6.000005000 5 6.00001 6.000010000
6.000000500 6 6.000001 6.000001000
6.000000050 7 6.0000001 6.000000100
6.000000005 8 6.00000001 6.000000010
7.500000000 0 8 8.000000000
7.050000000 1 7.1 7.100000000
7.005000000 2 7.01 7.010000000
7.000500000 3 7.001 7.001000000
7.000050000 4 7.0001 7.000100000
7.000005000 5 7.00001 7.000010000
7.000000500 6 7.000001 7.000001000
7.000000050 7 7.0000001 7.000000100
7.000000005 8 7.00000001 7.000000010
8.500000000 0 9 9.000000000
8.050000000 1 8.1 8.100000000
8.005000000 2 8.01 8.010000000
8.000500000 3 8.001 8.001000000
8.000050000 4 8.0001 8.000100000
8.000005000 5 8.00001 8.000010000
8.000000500 6 8.000001 8.000001000
8.000000050 7 8.0000001 8.000000100
8.000000005 8 8.00000001 8.000000010
9.500000000 0 10 10.000000000
9.050000000 1 9.1 9.100000000
9.005000000 2 9.01 9.010000000
9.000500000 3 9.001 9.001000000
9.000050000 4 9.0001 9.000100000
9.000005000 5 9.00001 9.000010000
9.000000500 6 9.000001 9.000001000
9.000000050 7 9.0000001 9.000000100
9.000000005 8 9.00000001 9.000000010
Press any key to continue . . .
X# round() behaviour
Posted: Mon Apr 22, 2019 9:55 pm
by Jamal
Hi Karl,
>> BTW. I´m talking about the type float, so please no discussion about the type decimal
<<
So, as turned out, it was all about the decimal type
and one of the longest discussion on this forum!
Jamal
X# round() behaviour
Posted: Tue Apr 23, 2019 4:13 am
by Karl-Heinz
Jamal wrote:Hi Karl,
>> BTW. I´m talking about the type float, so please no discussion about the type decimal
<<
So, as turned out, it was all about the decimal type
and one of the longest discussion on this forum!
Jamal
Hi Jamal
you really don´t understand what i wanted to say ? . i´m using in my VO programs "floats" all over the place and i wanted to avoid the "brilliant" tip: Drop "floats" and use "Decimals"
But i hope you have at least learned how to use negative rounding.
regards
Karl-Heinz
X# round() behaviour
Posted: Tue Apr 23, 2019 5:51 am
by Jamal
Hi Karl,
Thanks to you, your post uncovered issues hidden inside the Round function and maybe in other areas. The important thing here to ensure compatibility with the behavior of VO. X# being Open Source helps in collaborating and having lively discussions. Please see the other thread where Johan provided REAL8 argument and return value and I provided an alternate VO Round() function which uses USUAL for the argument and return value. May be it's not "brilliant", but hopefully in the right direction to handle Floats or whatever numeric variable type you throw at it.
BTW, thanks for the sample about negative rounding; I learn something new everyday.