我这是代码,它围绕62到61并在输出中显示它。为什么它决定舍入以及如何在输出中获得62?
var d: double;
i: integer;
begin
d:=0.62;
i:= trunc(d*100);
Showmessage( inttostr(i) );
end;
答案 0 :(得分:14)
这归结为0.62
在二进制浮点数据类型中不能完全表示的事实。 closest representable double value to 0.62
是:
0.61999 99999 99999 99555 91079 01499 37383 83054 73327 63671 875
将此值乘以100时,结果值略小于62.接下来会发生什么取决于如何处理中间值d*100
。在您的程序中,在具有默认设置的32位Windows编译器下,中间值保存在80位扩展寄存器中。最接近的80位扩展精度值为:
61.99999 99999 99999 55591 07901 49937 38383 05473 32763 67187 5
由于该值小于62,Trunc
从Trunc
舍入为零后返回61.
如果您将d*100
存储为双倍值,那么您会看到不同的结果。
d := 0.62;
d := d*100;
i := Trunc(d);
Writeln(i);
这个程序输出62而不是61.那是因为虽然扩展80位精度的d*100
小于62,但closest double precision value to that 80 bit value实际上是62.
类似地,如果使用64位编译器编译原始程序,则在没有80位寄存器的SSE单元中执行算术运算。所以没有80位中间值,你的程序输出62。
或者,回到32位编译器,您可以安排在FPU上将中间值存储为64位精度,并且还可以实现62的输出。调用Set8087CW($1232)
来实现此目的。
正如您所看到的,二进制浮点运算有时会令人惊讶。
如果您使用Round
而不是Trunc
,则返回的值将是最接近的整数,而不是Trunc
向零舍入。
但也许更好的解决方案是使用十进制数据类型而不是二进制数据类型。如果你这样做,那么你可以准确地代表0.62,从而避免所有这些问题。 Delphi内置的十进制实值数据类型为Currency
。
答案 1 :(得分:4)
使用round
代替trunc
。
round
将向最接近的整数舍入,62.00
非常接近62
,因此没有问题。 trunc
将向最接近的整数舍入为零,62.00
非常接近61.9999999
,因此数字' fuzz'可能会导致你描述的问题。