Watch, Follow, &
Connect with Us

Developer Tools Community.

Welcome, Guest
Guest Settings

Thread: Why RoundTo behaves differently in D7 and XE6 and not consistent to doco?

Replies: 4 - Last Post: Aug 29, 2014 12:36 AM Last Post By: Jer Min Kok
 Posts: 3 Registered: 7/31/14
 Why RoundTo behaves differently in D7 and XE6 and not consistent to doco? Posted: Aug 26, 2014 10:46 PM I have notice the RoundTo() function in both Delphi 7 and XE6 behave differently with some numbers and neither of them ad head to banker’s method as they said in the documentation. I pass the following number series to RoundTo(val, -4) in Delphi 7 and XE6 and they return different result for some numbers, could you explain why? Value Dephi7 XE6 69.72505 69.7250 69.7250 69.72515 69.7251 69.7251 69.72525 69.7252 69.7253 <-- diff 69.72535 69.7254 69.7254 69.72545 69.7254 69.7254 69.72555 69.7255 69.7255 69.72565 69.7256 69.7257 <-- diff 69.72575 69.7258 69.7258 69.72585 69.7258 69.7258 69.72595 69.7259 69.7259 This is the delphi code i used to produce the numbers above. I used the code to compile in Delphi 7 and XE6, and got different result. procedure TForm1.Button1Click(Sender: TObject); const valueList : Array[0..9] of double = ( 69.72505, 69.72515, 69.72525, 69.72535, 69.72545, 69.72555, 69.72565, 69.72575, 69.72585, 69.72595 ); var d : double; i : Integer; begin Memo1.Clear; for i := Low(valueList) to High(valueList) do Memo1.Lines.Add(FloatToStr(valueList[i]) + ', ' + FloatToStr(RoundTo(valueList[i], -4))); end; However, if I do it in c# using Round(val, 4), these are what I get, which is consistent to banker’s method: Value Round 69.72505 69.7250 69.72515 69.7252 69.72525 69.7252 69.72535 69.7254 69.72545 69.7254 69.72555 69.7256 69.72565 69.7256 69.72575 69.7258 69.72585 69.7258 69.72595 69.7260 One more thing, with the following code, which is the example from delphi documentation: ShowMessage(FloatToStr(RoundTo(1.245, -2))); Delphi 7 gives me 1.25 and XE6 gives me 1.24, which contradicted to their documentations. (delphi 7 doco says 1.24 but XE6 doco says 1.25). Could you explain why?
 Posts: 1,529 Registered: 9/23/99
 Re: Why RoundTo behaves differently in D7 and XE6 and not consistent to doco? Posted: Aug 27, 2014 10:25 AM   in response to: Jer Min Kok Jer Min Kok wrote: I have notice the RoundTo() function in both Delphi 7 and XE6 behave differently with some numbers and neither of them ad head to banker’s method as they said in the documentation. I pass the following number series to RoundTo(val, -4) in Delphi 7 and XE6 and they return different result for some numbers, could you explain why? Value Dephi7 XE6 69.72505 69.7250 69.7250 69.72515 69.7251 69.7251 69.72525 69.7252 69.7253 <-- diff 69.72535 69.7254 69.7254 69.72545 69.7254 69.7254 69.72555 69.7255 69.7255 69.72565 69.7256 69.7257 <-- diff 69.72575 69.7258 69.7258 69.72585 69.7258 69.7258 69.72595 69.7259 69.7259 This is the delphi code i used to produce the numbers above. I used the code to compile in Delphi 7 and XE6, and got different result. procedure TForm1.Button1Click(Sender: TObject); const valueList : Array[0..9] of double = ( 69.72505, 69.72515, 69.72525, 69.72535, 69.72545, 69.72555, 69.72565, 69.72575, 69.72585, 69.72595 ); var d : double; i : Integer; begin Memo1.Clear; for i := Low(valueList) to High(valueList) do Memo1.Lines.Add(FloatToStr(valueList[i]) + ', ' + FloatToStr(RoundTo(valueList[i], -4))); end; However, if I do it in c# using Round(val, 4), these are what I get, which is consistent to banker’s method: Value Round 69.72505 69.7250 69.72515 69.7252 69.72525 69.7252 69.72535 69.7254 69.72545 69.7254 69.72555 69.7256 69.72565 69.7256 69.72575 69.7258 69.72585 69.7258 69.72595 69.7260 One more thing, with the following code, which is the example from delphi documentation: ShowMessage(FloatToStr(RoundTo(1.245, -2))); Delphi 7 gives me 1.25 and XE6 gives me 1.24, which contradicted to their documentations. (delphi 7 doco says 1.24 but XE6 doco says 1.25). Could you explain why? what you are seeing are slight loss of precision in the conversion from double (8 bytes) to extended (10 bytes) which is what RoundTo takes. When you are testing boundaries (.5) like that just a .00000000001 low or high will impact the result. If you convert you array from an Array of double to an array of extended eliminating the conversion between the two types and subsequent loss of precision everything outputs as expected for me in XE6. -- Jeff Overcash (TeamB) (Please do not email me directly unless asked. Thank You) And so I patrol in the valley of the shadow of the tricolor I must fear evil. For I am but mortal and mortals can only die. Asking questions, pleading answers from the nameless faceless watchers that stalk the carpeted corridors of Whitehall. (Fish)
 Posts: 3 Registered: 7/31/14
 Re: Why RoundTo behaves differently in D7 and XE6 and not consistent to doco? Posted: Aug 27, 2014 4:56 PM   in response to: Jeff Overcash (... Thanks for the response, Jeff. After i changed the array of double to extended and they have more differences. Delphi 7 seems consistent to the previous result but XE6 is quite different. Here are the results Value D7 XE6 69.72505 69.7250 69.7250 69.72515 69.7251 69.7252 <--diff 69.72525 69.7252 69.7252 69.72535 69.7254 69.7254 69.72545 69.7254 69.7254 69.72555 69.7255 69.7256 <--diff 69.72565 69.7256 69.7257 <--diff 69.72575 69.7258 69.7258 69.72585 69.7258 69.7258 69.72595 69.7259 69.7260 <--diff here is my updated code: procedure TForm1.Button1Click(Sender: TObject); const valueList : Array[0..9] of extended = ( 69.72505, 69.72515, 69.72525, 69.72535, 69.72545, 69.72555, 69.72565, 69.72575, 69.72585, 69.72595 ); var i : Integer; begin Memo1.Clear; for i := Low(valueList) to High(valueList) do Memo1.Lines.Add(FloatToStr(valueList[i]) + ', ' + FloatToStr(RoundTo(valueList[i], -4))); end; I understand the loss of precision problem in floating point converstion, but why are they behave differently in both version? Again, neither of these consistent to banker's rounding which is specified in the documentation. But XE6 seems closer to banker's rounding result, although still not exactly. Banker's rounding should round to the closest even number, which is also used by c#/.NET Round() method and c# could produce the result consistent to what i expected to this rounding method.(see the c# output in my original post above) Also, with the simple test from the example from the documentation, they both return different result: ShowMessage(FloatToStr(RoundTo(1.245, -2))); Delphi 7 returns 1.25 and XE6 returns 1.24, however the delphi 7 document says it is 1.24 but XE6 document says it is 1.25 what you are seeing are slight loss of precision in the conversion from double (8 bytes) to extended (10 bytes) which is what RoundTo takes. When you are testing boundaries (.5) like that just a .00000000001 low or high will impact the result. If you convert you array from an Array of double to an array of extended eliminating the conversion between the two types and subsequent loss of precision everything outputs as expected for me in XE6. -- Jeff Overcash (TeamB) (Please do not email me directly unless asked. Thank You) And so I patrol in the valley of the shadow of the tricolor I must fear evil. For I am but mortal and mortals can only die. Asking questions, pleading answers from the nameless faceless watchers that stalk the carpeted corridors of Whitehall. (Fish)
 Posts: 1,529 Registered: 9/23/99
 Re: Why RoundTo behaves differently in D7 and XE6 and not consistent to doco? Posted: Aug 27, 2014 6:21 PM   in response to: Jer Min Kok Jer Min Kok wrote: I understand the loss of precision problem in floating point converstion, but why are they behave differently in both version? RoundTo in D7 was a pure pascal implementation, the one in XE6 is based on the FastCode one which is asm. The Delphi 7 version expects doubles to be passed in, the XR6 Extended. If you take the XE6 RoundTo and use it in D7 you get the same results (have to comment out the SetRoundMode stuff). The D7 version does a Power of -scale, round then a division, the XE6 version multiples by 1E+04, round then multiplies by 1E-04. Floating point numbers are very volatile to slight variations. The XE6 version is more reliable in your test case. Of the 4 differences, XE6 is right on 1,2,4 and D7 only on 3. Once again this is probably more explained in the fact that the floating point number can not be accurately represented in in 8 or 10 bytes so you are actually starting with a very close approximation. If you need exact floating point numbers you probably should be using BCD data types that is the main reason BCD exists. Taking your original code with the double and showing jsut how it changes slightly in converting to an Extened look at this program Project3;   {\$APPTYPE CONSOLE}   {\$R *.res}   uses System.SysUtils, System.Math;   const valueList : Array[0..9] of Double = ( 69.72505, 69.72515, 69.72525, 69.72535, 69.72545, 69.72555, 69.72565, 69.72575, 69.72585, 69.72595 );   var e : Extended; i : Integer; i1 : Int64; begin for I := 0 to 9 do begin e := valueList[i] * 1e+04; Writeln(E:20:18); i1 := Round(E); WriteLn(i1); e := i1 * 1e-04; Writeln(E:20:18); Writeln; end; Readln; end. Notice that absolutely none of your data end exactly in 5 like it was as a double const, it is either up or down by ~0.000000000001, but that is enough so that round can be affected. -- Jeff Overcash (TeamB) (Please do not email me directly unless asked. Thank You) And so I patrol in the valley of the shadow of the tricolor I must fear evil. For I am but mortal and mortals can only die. Asking questions, pleading answers from the nameless faceless watchers that stalk the carpeted corridors of Whitehall. (Fish)
 Posts: 3 Registered: 7/31/14
 Re: Why RoundTo behaves differently in D7 and XE6 and not consistent to doco? Posted: Aug 29, 2014 12:35 AM   in response to: Jeff Overcash (... Thanks Jeff, that's really answered my question. The example code you shown is a really GOOD illustration of the problem cheers Edited by: Jer Min Kok on Aug 29, 2014 12:36 AM
Legend