gpt4 book ai didi

c - Delphi和MSVC不会以相同的方式将+ NAN与零进行比较

转载 作者:行者123 更新时间:2023-12-03 13:49:25 25 4
gpt4 key购买 nike

我将C代码移植到Delphi中,发现编译器(Delphi 10.4.1和MSVC2019,均面向x32平台)处理+ NAN到零的比较方式时遇到问题。两种编译器均使用IEEE754表示形式来表示双浮点值。我发现了这个问题,因为到Delphi的C代码I端口附带了一大堆数据,以验证代码的正确性。
原始源代码很复杂,但是我能够在Delphi和C语言中产生最小的可重现应用程序。
C代码:

#include <stdio.h>
#include <math.h>

double AngRound(double x) {
const double z = 1 / (double)(16);
volatile double y;
if (x == 0)
return 0;
y = fabs(x);
/* The compiler mustn't "simplify" z - (z - y) to y */
if (y < z)
y = z - (z - y); // <= This line is *NOT* executed
if (x < 0)
return -y;
else
return y; // <= This line is executed
}

union {
double d;
int bits[2];
} u;


int main()
{
double lon12;
double ar;
int lonsign;

// Create a +NAN number IEEE754
u.bits[0] = 0;
u.bits[1] = 0x7ff80000;

lon12 = u.d; // Debugger shows lon12 is +nan
if (lon12 >= 0)
lonsign = 1;
else
lonsign = -1; // <= This line is executed
// Now lonsign is -1

ar = AngRound(lon12);
// Now ar is +nan

lon12 = lonsign * ar;
// Now lon12 is +nan
}
Delphi代码:
program NotANumberTest;

{$APPTYPE CONSOLE}

{$R *.res}

uses
System.SysUtils;

type
TRec = record
case t : Boolean of
TRUE: (d : Double);
FALSE: (bits : array [0..1] of Cardinal);
end;

function AngRound(x : Double) : Double;
const
z : Double = 1 / Double(16);
var
y : Double;
begin
if x = 0 then
Result := 0
else begin
y := abs(x);
if y < z then
// The compiler mustn't "simplify" z - (z - y) to y
y := z - (z - y); // <= This line is executed
if x < 0 then
Result := -y // <= This line is executed
else
Result := y;
end;
end;

var
u : TRec;
lon12 : Double;
lonsign : Integer;
ar : Double;
begin
// Create a +NAN number IEEE754
u.bits[0] := 0;
u.bits[1] := $7ff80000;

lon12 := u.d; // Debugger shows lon12 is +NAN
if lon12 >= 0 then
lonsign := 1 // <= This line is executed
else
lonsign := -1;
// Now lonsign is +1

ar := AngRound(lon12);
// Now ar is -NAN

lon12 := lonsign * ar;
// Now lon12 is -NAN
end.
我已经将行标记为比较后执行的。
当lon12变量等于+ NAN时,Delphi将lon12> = 0评估为TRUE。
当lon12变量等于+ NAN时,MSVC将lon12> = 0评估为FALSE。
lonsign在C和Delphi中具有不同的值。
接收+ NAN作为参数的AngRound返回不同的值。
lon12的最终值(致命)不同。
编译器生成的机器代码是不同的:
Delphi生成的机器代码:
Delphi generated machinecode
MSVC2019生成的机器代码:
MSVC2019 generated machine code
在Delphi中,比较结果似乎更合乎逻辑:当lon12为+ NAN时,(lon12> = 0)为TRUE。这是否意味着该错误存在于MSVC2019编译器中?我是否应该考虑原始C代码的测试数据集有误?

最佳答案

首先,您的Delphi程序的行为不符合您的描述,至少在我容易使用的Delphi版本XE7上。当您的程序运行时,将引发无效的操作浮点异常。我将假设您实际上掩盖了浮点异常。
更新:事实证明,在XE7和10.3之间的某个时间,Delphi 32位代码生成器从fcom切换到fucom,这解释了为什么XE7设置IA浮点异常,而10.3则没有。
您的Delphi代码远非最少。让我们尝试做一个真正最小的例子。让我们看一下其他比较运算符。

{$APPTYPE CONSOLE}

uses
System.Math;

var
d: Double;
begin
SetFPUExceptionMask(exAllArithmeticExceptions);
SetSSEExceptionMask(exAllArithmeticExceptions);
d := NaN;
Writeln(d > 0);
Writeln(d >= 0);
Writeln(d < 0);
Writeln(d <= 0);
Writeln(d = d);
Writeln(d <> d);
end.
在XE7中的32位以下,此输出
真的
真的
错误的
错误的
真的
错误的

在10.3.3(以及您在下面的评论中报告的10.4.1)的32位下,此输出
真的
真的
真的
真的
错误的
真的

在XE7和10.3.3(和10.4.1作为报告)中的64位以下,此输出
错误的
错误的
错误的
错误的
错误的
真的

64位输出正确。两种变体的32位输出均不正确。我们可以通过引用 What is the rationale for all comparisons returning false for IEEE754 NaN values?看到这一点

all comparisons with the operators ==, <=, >=, <, > where one or both values is NaN returns false, contrary to the behaviour of all other values.


对于您的32位Delphi代码,您将需要解决此错误,并在需要处理此类比较时包括特殊情况的代码。当然,除非有一定的机会,否则您正在使用10.4并且它已经解决了该问题。

关于c - Delphi和MSVC不会以相同的方式将+ NAN与零进行比较,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65991239/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com