Watch, Follow, &
Connect with Us

For forums, blogs and more please visit our
Developer Tools Community.


Welcome, Guest
Guest Settings
Help

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


This question is answered. Helpful answers available: 2. Correct answers available: 1.


Permlink Replies: 4 - Last Post: Aug 29, 2014 12:36 AM Last Post By: Jer Min Kok
Jer Min Kok

Posts: 3
Registered: 7/31/14
Why RoundTo behaves differently in D7 and XE6 and not consistent to doco?  
Click to report abuse...   Click to reply to this thread Reply
  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?
Jeff Overcash (...

Posts: 1,529
Registered: 9/23/99
Re: Why RoundTo behaves differently in D7 and XE6 and not consistent to doco?  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Aug 27, 2014 10:25 AM   in response to: Jer Min Kok 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)
Jer Min Kok

Posts: 3
Registered: 7/31/14
Re: Why RoundTo behaves differently in D7 and XE6 and not consistent to doco?  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Aug 27, 2014 4:56 PM   in response to: Jeff Overcash (... 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)
Jeff Overcash (...

Posts: 1,529
Registered: 9/23/99
Re: Why RoundTo behaves differently in D7 and XE6 and not consistent to doco?  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Aug 27, 2014 6:21 PM   in response to: Jer Min Kok 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)
Jer Min Kok

Posts: 3
Registered: 7/31/14
Re: Why RoundTo behaves differently in D7 and XE6 and not consistent to doco?  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Aug 29, 2014 12:35 AM   in response to: Jeff Overcash (... 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
Helpful Answer (5 pts)
Correct Answer (10 pts)

Server Response from: ETNAJIVE02