是否有编译器设置来控制如何在 Delphi 中键入浮点文字?
Is there a compiler setting to control how floating point literals are typed in Delphi?
虽然 e
的情况在默认情况下有效,但我想更改文字 0.1
的默认转换以允许 r
在不修改任何代码的情况下工作。这是否可以通过编译器选项、编译器指令或其他方式实现?
procedure Test;
var
s : Single;
r : Real;
d : Double;
e : Extended;
begin
s := 0.1;
if (s = 0.1) then ShowMessage('s matched'); // fail
r := 0.1;
if (r = 0.1) then ShowMessage('r matched'); // fail
d := 0.1;
if (d = 0.1) then ShowMessage('d matched'); // fail
e := 0.1;
if (e = 0.1) then ShowMessage('e matched'); // pass
end;
我不知道。 x87 协处理器默认在扩展模式下工作,因此编译器会进行调整。
请注意,如果您针对 64 位 r、d 和 e 匹配编译此程序(可能是因为在 64 位上 real=double=extended)。 64 位代码不为此使用 x87,而是根据 64 位 ABI 使用 SSE2。
问题不在于转换,而在于比较本身,以及浮点数的编码方式。参见例如this blog article, which deals explicitely with the 0.1 value。简而言之,这个 0.1 值在 IEEE-754 格式中未编码为 0.1,而是编码为 0.100000001490116119384765625(单)或 0.1000000000000000555111512312578270211815834045(双)...
其实就是下面一行
if (s = 0.1) then ShowMessage('s matched'); // fail
被编译(至少在 x87 下,即 Delphi 32 位)为
s := 0.1;
e := 0.1;
if s=e then
writeln('ok');
并且在IEEE标准编码中,0.1不存储为扩展精度或单精度:
s := 0.1;
e := 0.1;
writeln('s=',BinToHex(@s, SizeOf(s)));
writeln('e=',BinToHex(@e, SizeOf(e)));
//s=CDCCCC3D
//e=CDCCCCCCCCCCCCCCFB3F
而 0.5 没有舍入问题:
s := 0.5;
e := 0.5;
writeln('s=',BinToHex(@s, SizeOf(s)));
writeln('e=',BinToHex(@e, SizeOf(e)));
if s=e then
writeln(ok); // it works!
// s=0000003F
// e=0000000000000080FE3F
你可以这样强制比较:
var s, s2: single;
s := 0.1;
s2 := 0.1;
if s = s2 then
writeln('ok');
但无论如何,要正确比较浮点值,可以使用 Math.pas
单元的 SameValue()
方法,并使用适当的 epsilon。
没有编译器开关可以满足您的要求。问题是 32 位 Windows 编译器将浮点文字表示为 10 字节扩展精度值。并且因为 0.1
不能完全表示,所以不等于 0.1
.
的 8 字节双精度表示
If constantExpression is a real, its type is Extended.
然而,您可以通过使用类型化常量来获得所需的结果。例如:
const
TenthDouble: Double = 0.1;
var
d: Double;
....
d := 0.1;
if d = TenthDouble then
....
通过使用类型化常量,我们能够强制编译器将常量设为 0.1
.
的 8 字节双精度表示形式
虽然 e
的情况在默认情况下有效,但我想更改文字 0.1
的默认转换以允许 r
在不修改任何代码的情况下工作。这是否可以通过编译器选项、编译器指令或其他方式实现?
procedure Test;
var
s : Single;
r : Real;
d : Double;
e : Extended;
begin
s := 0.1;
if (s = 0.1) then ShowMessage('s matched'); // fail
r := 0.1;
if (r = 0.1) then ShowMessage('r matched'); // fail
d := 0.1;
if (d = 0.1) then ShowMessage('d matched'); // fail
e := 0.1;
if (e = 0.1) then ShowMessage('e matched'); // pass
end;
我不知道。 x87 协处理器默认在扩展模式下工作,因此编译器会进行调整。
请注意,如果您针对 64 位 r、d 和 e 匹配编译此程序(可能是因为在 64 位上 real=double=extended)。 64 位代码不为此使用 x87,而是根据 64 位 ABI 使用 SSE2。
问题不在于转换,而在于比较本身,以及浮点数的编码方式。参见例如this blog article, which deals explicitely with the 0.1 value。简而言之,这个 0.1 值在 IEEE-754 格式中未编码为 0.1,而是编码为 0.100000001490116119384765625(单)或 0.1000000000000000555111512312578270211815834045(双)...
其实就是下面一行
if (s = 0.1) then ShowMessage('s matched'); // fail
被编译(至少在 x87 下,即 Delphi 32 位)为
s := 0.1;
e := 0.1;
if s=e then
writeln('ok');
并且在IEEE标准编码中,0.1不存储为扩展精度或单精度:
s := 0.1;
e := 0.1;
writeln('s=',BinToHex(@s, SizeOf(s)));
writeln('e=',BinToHex(@e, SizeOf(e)));
//s=CDCCCC3D
//e=CDCCCCCCCCCCCCCCFB3F
而 0.5 没有舍入问题:
s := 0.5;
e := 0.5;
writeln('s=',BinToHex(@s, SizeOf(s)));
writeln('e=',BinToHex(@e, SizeOf(e)));
if s=e then
writeln(ok); // it works!
// s=0000003F
// e=0000000000000080FE3F
你可以这样强制比较:
var s, s2: single;
s := 0.1;
s2 := 0.1;
if s = s2 then
writeln('ok');
但无论如何,要正确比较浮点值,可以使用 Math.pas
单元的 SameValue()
方法,并使用适当的 epsilon。
没有编译器开关可以满足您的要求。问题是 32 位 Windows 编译器将浮点文字表示为 10 字节扩展精度值。并且因为 0.1
不能完全表示,所以不等于 0.1
.
If constantExpression is a real, its type is Extended.
然而,您可以通过使用类型化常量来获得所需的结果。例如:
const
TenthDouble: Double = 0.1;
var
d: Double;
....
d := 0.1;
if d = TenthDouble then
....
通过使用类型化常量,我们能够强制编译器将常量设为 0.1
.