64 位和 32 位 Round 之间的浮点差异
Floating point differences between 64 bit and 32 bit with Round
我知道所有关于浮点数的近似问题,所以我知道如果 4.5 被近似为 4.4999999999999991,它是如何四舍五入到 4 的。我的问题是为什么在 32 位和 64 位上使用相同的类型会有所不同。
在下面的代码中我有两个计算。在 32 位中,MyRoundValue1 的值为 4,MyRoundValue2 的值为 5。在 64 位中,它们都是 4。结果不应该与 32 位和 64 位一致吗?
{$APPTYPE CONSOLE}
const
MYVALUE1: Double = 4.5;
MYVALUE2: Double = 5;
MyCalc: Double = 0.9;
var
MyRoundValue1: Integer;
MyRoundValue2: Integer;
begin
MyRoundValue1 := Round(MYVALUE1);
MyRoundValue2 := Round(MYVALUE2 * MyCalc);
WriteLn(IntToStr(MyRoundValue1));
WriteLn(IntToStr(MyRoundValue2));
end.
System.Round 内部接受一个 Extended 值。在 32 位中,FPU 内部的计算是 Extended。在 64 位中 Extended 类似于 Double。内部表示可能只是差异很大才能产生差异。
在 x87 中此代码:
MyRoundValue2 := Round(MYVALUE2 * MyCalc);
编译为:
MyRoundValue2 := Round(MYVALUE2 * MyCalc);
0041C4B2 DD0508E64100 fld qword ptr [[=15=]41e608]
0041C4B8 DC0D10E64100 fmul qword ptr [[=15=]41e610]
0041C4BE E8097DFEFF call @ROUND
0041C4C3 A3C03E4200 mov [[=15=]423ec0],eax
Delphi RTL 下 x87 单元的默认控制字执行 80 位精度的计算。所以浮点数单元乘以 5 乘以 closest 64 bit value to 0.9 即:
0.90000 00000 00000 02220 44604 92503 13080 84726 33361 81640 625
请注意,此值大于 0.9。事实证明,当乘以 5 并四舍五入到最接近的 80 位值时,该值大于 4.5。因此 Round(MYVALUE2 * MyCalc)
returns 5.
在 64 位上,浮点数学是在 SSE 单元上完成的。那不使用 80 位中间值。事实证明,最接近 0.9 的 double 的 5 倍,四舍五入到 double 精度恰好是 4.5。因此 Round(MYVALUE2 * MyCalc)
returns 4 在 64 位上。
您可以说服 32 位编译器以与 64 位编译器相同的方式存储,而不是依赖于中间的 80 位值:
{$APPTYPE CONSOLE}
const
MYVALUE1: Double = 4.5;
MYVALUE2: Double = 5;
MyCalc: Double = 0.9;
var
MyRoundValue1: Integer;
MyRoundValue2: Integer;
d: Double;
begin
MyRoundValue1 := Round(MYVALUE1);
d := MYVALUE2 * MyCalc;
MyRoundValue2 := Round(d);
WriteLn(MyRoundValue1);
WriteLn(MyRoundValue2);
end.
此程序产生与您的 64 位程序相同的输出。
或者您可以强制 x87 单元使用 64 位中间体。
{$APPTYPE CONSOLE}
uses
SysUtils;
const
MYVALUE1: Double = 4.5;
MYVALUE2: Double = 5;
MyCalc: Double = 0.9;
var
MyRoundValue1: Integer;
MyRoundValue2: Integer;
begin
Set8087CW(32); // <-- round intermediates to 64 bit
MyRoundValue1 := Round(MYVALUE1);
MyRoundValue2 := Round(MYVALUE2 * MyCalc);
WriteLn(MyRoundValue1);
WriteLn(MyRoundValue2);
end.
我知道所有关于浮点数的近似问题,所以我知道如果 4.5 被近似为 4.4999999999999991,它是如何四舍五入到 4 的。我的问题是为什么在 32 位和 64 位上使用相同的类型会有所不同。
在下面的代码中我有两个计算。在 32 位中,MyRoundValue1 的值为 4,MyRoundValue2 的值为 5。在 64 位中,它们都是 4。结果不应该与 32 位和 64 位一致吗?
{$APPTYPE CONSOLE}
const
MYVALUE1: Double = 4.5;
MYVALUE2: Double = 5;
MyCalc: Double = 0.9;
var
MyRoundValue1: Integer;
MyRoundValue2: Integer;
begin
MyRoundValue1 := Round(MYVALUE1);
MyRoundValue2 := Round(MYVALUE2 * MyCalc);
WriteLn(IntToStr(MyRoundValue1));
WriteLn(IntToStr(MyRoundValue2));
end.
System.Round 内部接受一个 Extended 值。在 32 位中,FPU 内部的计算是 Extended。在 64 位中 Extended 类似于 Double。内部表示可能只是差异很大才能产生差异。
在 x87 中此代码:
MyRoundValue2 := Round(MYVALUE2 * MyCalc);
编译为:
MyRoundValue2 := Round(MYVALUE2 * MyCalc); 0041C4B2 DD0508E64100 fld qword ptr [[=15=]41e608] 0041C4B8 DC0D10E64100 fmul qword ptr [[=15=]41e610] 0041C4BE E8097DFEFF call @ROUND 0041C4C3 A3C03E4200 mov [[=15=]423ec0],eax
Delphi RTL 下 x87 单元的默认控制字执行 80 位精度的计算。所以浮点数单元乘以 5 乘以 closest 64 bit value to 0.9 即:
0.90000 00000 00000 02220 44604 92503 13080 84726 33361 81640 625
请注意,此值大于 0.9。事实证明,当乘以 5 并四舍五入到最接近的 80 位值时,该值大于 4.5。因此 Round(MYVALUE2 * MyCalc)
returns 5.
在 64 位上,浮点数学是在 SSE 单元上完成的。那不使用 80 位中间值。事实证明,最接近 0.9 的 double 的 5 倍,四舍五入到 double 精度恰好是 4.5。因此 Round(MYVALUE2 * MyCalc)
returns 4 在 64 位上。
您可以说服 32 位编译器以与 64 位编译器相同的方式存储,而不是依赖于中间的 80 位值:
{$APPTYPE CONSOLE}
const
MYVALUE1: Double = 4.5;
MYVALUE2: Double = 5;
MyCalc: Double = 0.9;
var
MyRoundValue1: Integer;
MyRoundValue2: Integer;
d: Double;
begin
MyRoundValue1 := Round(MYVALUE1);
d := MYVALUE2 * MyCalc;
MyRoundValue2 := Round(d);
WriteLn(MyRoundValue1);
WriteLn(MyRoundValue2);
end.
此程序产生与您的 64 位程序相同的输出。
或者您可以强制 x87 单元使用 64 位中间体。
{$APPTYPE CONSOLE}
uses
SysUtils;
const
MYVALUE1: Double = 4.5;
MYVALUE2: Double = 5;
MyCalc: Double = 0.9;
var
MyRoundValue1: Integer;
MyRoundValue2: Integer;
begin
Set8087CW(32); // <-- round intermediates to 64 bit
MyRoundValue1 := Round(MYVALUE1);
MyRoundValue2 := Round(MYVALUE2 * MyCalc);
WriteLn(MyRoundValue1);
WriteLn(MyRoundValue2);
end.