是否有编译器设置来控制如何在 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 字节双精度表示

documentation 说:

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 字节双精度表示形式