X# round() behaviour

This forum is meant for questions and discussions about the X# language and tools
User avatar
Chris
Posts: 4926
Joined: Thu Oct 08, 2015 7:48 am
Location: Greece

X# round() behaviour

Post 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.
Chris Pyrgas

XSharp Development Team
chris(at)xsharp.eu
User avatar
lumberjack
Posts: 727
Joined: Fri Sep 25, 2015 3:11 pm
Location: South Africa

X# round() behaviour

Post 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
______________________
Johan Nel
Boshof, South Africa
User avatar
lumberjack
Posts: 727
Joined: Fri Sep 25, 2015 3:11 pm
Location: South Africa

X# round() behaviour

Post by lumberjack »

Chris,
Are the runtime forcing a cast to decimal before the rounding?
______________________
Johan Nel
Boshof, South Africa
FFF
Posts: 1582
Joined: Fri Sep 25, 2015 4:52 pm
Location: Germany

X# round() behaviour

Post 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
Regards
Karl
(on Win8.1/64, Xide32 2.20, X#2.20.0.3)
User avatar
Chris
Posts: 4926
Joined: Thu Oct 08, 2015 7:48 am
Location: Greece

X# round() behaviour

Post by Chris »

Johan, please see my sample that demonstrates the problem, it does not use bankers rounding.
Chris Pyrgas

XSharp Development Team
chris(at)xsharp.eu
Jamal
Posts: 322
Joined: Mon Jul 03, 2017 7:02 pm

X# round() behaviour

Post 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
User avatar
lumberjack
Posts: 727
Joined: Fri Sep 25, 2015 3:11 pm
Location: South Africa

X# round() behaviour

Post 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 . . .
______________________
Johan Nel
Boshof, South Africa
Jamal
Posts: 322
Joined: Mon Jul 03, 2017 7:02 pm

X# round() behaviour

Post 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
Karl-Heinz
Posts: 774
Joined: Wed May 17, 2017 8:50 am
Location: Germany

X# round() behaviour

Post 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
Jamal
Posts: 322
Joined: Mon Jul 03, 2017 7:02 pm

X# round() behaviour

Post 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.
Post Reply