Delphi FreePascal中的Max函数产生意外结果

Delphi FreePascal中的Max函数产生意外结果,delphi,freepascal,Delphi,Freepascal,运行中的示例非常容易理解: program Project1; uses SysUtils, Math; var fValue: double; fValueMax: double; begin fValue := 7.0207503445953527; fValueMax := Max(0, fValue); writeln(fValue); writeln(fValueMax); readln; end. 然而,结果完全出乎意料。出于某种原因,Max函

运行中的示例非常容易理解:

program Project1;

uses
SysUtils, Math;

var
  fValue: double;
  fValueMax: double;
begin
  fValue := 7.0207503445953527;
  fValueMax := Max(0, fValue);
  writeln(fValue);
  writeln(fValueMax);
  readln;
end.   
然而,结果完全出乎意料。出于某种原因,Max函数不仅返回两个参数中较大的数字,而且还更改其值


在上面的示例代码中,fValueMax的期望值正好是fValue,但是fValueMax更大。差别大约是E-7,虽然很小,但仍然出人意料,并使我下面的代码崩溃(在这里发布这些代码不是为了让问题变得清晰和简单)。

我应该预先声明,我上次使用Pascal是在将近25年前。但出于好奇,我拉下了自由帕斯卡,并尝试了以下方法:

program Project1;

uses
SysUtils, Math;

var
  fValue: double;
  fValueMax: double;

  fSingle: single;

  fValue2: double;
  fValue2b: double;
  fValueMax2: double;

begin
  fValue := 7.0207503445953527; 
  fSingle := 7.0207503445953527;
  fValueMax := Max(0, fValue);

  writeln(fValue);       // prints 7.0207503445953527E+000
  writeln(fValueMax);    // prints 7.0207505226135254E+000

  writeln(fSingle);      // prints 7.020750523E+00

  fValue2 := 7.0207503445953527;
  fValue2b := 0.0;
  fValueMax2 := Max(fValue2b, fValue2);

  writeln(fValue2);      // prints 7.0207503445953527E+000
  writeln(fValueMax2);   // prints 7.0207503445953527E+000
  readln;
end.
我的前两个
writeln
命令显示的结果与您报告看到的结果相同。我怀疑
Max
返回的值的精度可能比
double
要低,所以我创建了
fSingle
,并将其指定为与
fValue
相同的文本,并且可以肯定的是,它的值看起来非常接近
fValueMax
中返回的值

最后,我没有用
fValue
和literal
0
调用
Max
,而是用两个
double
类型的变量调用它,其中一个变量我已经设置为
0.0
。在这种情况下,您可以看到输入(
fValue2
)和输出(
fValueMax2
)具有完全相同的值。因此,虽然我不知道Pascal确定调用哪个重载的规则是什么,但我想知道您最初对
Max
的调用是否以某种方式解析为接受两个
单个
值并返回相同值的版本


虽然您可能意识到这一点,但我不得不提出一个通常的警告,即像
single
double
这样的浮点类型不能始终准确地表示您希望它们的值

根据编译器和优化,变量实际上可能保存在浮点寄存器中,该寄存器的精度高于浮点变量类型。调用
Max
时,结果可能是通过这样一个类型得到的,因此精度不高。@LasseVågsætherKarlsen,这意味着问题(上面的代码)甚至不需要复制?六羟甲基三聚氰胺六甲醚。。。关于什么样的优化会像预期的那样工作,你有什么想法吗?出于某种原因,FreePascal选择了Max(single,single)而不是Max(double,double)。这对我来说没有意义,但它就是这么做的。您可以强制它使用Max(double,double),方法是将zero转换为double:Max(double(0),fValue)。或者使用浮点值0代替整数0:Max(0.0,fValue)@Lasse否。如果它使用更高精度的寄存器,那么精度就不会丢失。是的,我的评论更多地是关于它使用浮点cpu寄存器中的局部变量,这比仍然使用的类型具有更高的精度。这意味着cpu(fpu)和内存之间的任何转换都容易失去精度。但这不是问题所在,所以别介意我。看起来确实像FreePascal中的一个bug(它似乎选择了单精度重载,尽管第二个参数是双精度)。当为第一个参数指定0.0而不是0时,它确实选择了正确的重载。FWIW:kudos。从一个25年没有接触过Pascal的人那里,你的代码看起来仍然不错。