Both, Borland Pascal 7 and Delphi 2007 have got the procedure STR which takes a number, a length and precision and converts it to a string like this:
str(9.234:5:1, s); // -> s = ' 9.2'
All is fine if the rounding is non-ambiguous, but if it isn't (0.5 -> up or down?) there is a problem: It seems to depend on the floating point data type in BP but is apparently consistent in Delphi 2007:
BP:
var
e: extended;
d: double;
begin
d := 2.15;
e := 2.15;
str(d:5:1, s); { -> s = ' 2.1' }
str(e:5:1, s); { -> s = ' 2.2' }
{ but: }
d := 2.25
e := 2.25
str(d:5:1, s); { -> s = ' 2.3' }
str(e:5:1, s); { -> s = ' 2.3' }
I was unable to find any rule on how doubles are being rounded, while apparently extendeds are always rounded up.
Delphi 2007 apparently always rounds up independent of the data type.
Does anybody know how the rounding is done in BP for double values?
I want to know because I am in the middle of porting some Borland Pascal code that uses doubles to Delphi 2007 and when I compare the outputs I get inconsistencies that result from rounding in the STR procedure. These do not really matter for the result but it makes it very difficult to spot important differences.
The cases of d = 2.15 and d = 2.25 are different:
2.15 cannot be represented exactly in float format, and so it is impossible to say how the value is rounded without analyzing the binary representation of the float value in a given float format;
2.25 is exactly represented in float format, and the rounding result must be predictable;
I have tested rounding on some values which are exactly represented in float format and found that STR always rounds up for positive values and down for negative values. STR does not follow "banker's rounding", eg:
d := 2.25;
// d:= roundto(d, -1); banker's rounding is 2.2
str(d:5:1, s); { -> s = ' 2.3' }
d:= 2.75;
// d:= roundto(d, -1); banker's rounding is 2.8
str(d:5:1, s); { -> s = ' 2.8' }
I think the problem you are seeing is that many numbers that can be represented exactly in decimal notation can only be represented as repeating decimals (binimals?) in binary. So, it may be that 2.15 cannot be exactly represented by a double and that 2.14999999999234 (or something) is the closest you can get with a binary representation.
Since the closest binary representation of the number is strictly less than 2.15, the Str function rounds down instead of up.
Looks like a floating point rounding error. When you look at the assembly code generated in Delphi, you can see that _Str2Ext is called for both operations, which converts an Extended into a string. So in order to do that it has to convert your Double into an Extended behind the scenes:
Project1.dpr.16: str(d:5:1, s); { -> s = ' 2.1' }
0040E666 DD45E8 fld qword ptr [ebp-$18]
0040E669 83C4F4 add esp,-$0c
0040E66C DB3C24 fstp tbyte ptr [esp]
0040E66F 9B wait
And somewhere in the conversion from Double to Extended, you're losing a little bit of precision, and ending up with a slightly different number than if you'd declared the same number (as we read them) as an Extended to begin with. This is pretty common in floating-point conversions. Not sure if there's anything you can do about it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With