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
和literal0
调用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的人那里,你的代码看起来仍然不错。