C 코드를 Delphi로 이식 중이며 컴파일러 (Delphi 10.4.1 및 MSVC2019, 둘 다 x32 플랫폼을 대상으로 함)가 + NAN과 0의 비교를 처리하는 방식에 문제가 있습니다. 두 컴파일러 모두 이중 부동 소수점 값에 IEEE754 표현을 사용합니다. 델파이로의 C-Code 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
}
델파이 코드 :
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과 같을 때 (lon12> = 0)을 TRUE로 평가합니다. MSVC는 lon12 변수가 + NAN과 같을 때 (lon12> = 0)을 FALSE로 평가합니다.
lonsign은 C와 Delphi에서 다른 값을 가지고 있습니다.
+ NAN을 인수로받는 AngRound는 다른 값을 반환합니다.
lon12의 최종 값은 (치명적으로) 다릅니다.
컴파일러에 의해 생성 된 기계 코드는 다릅니다.
델파이가 생성 한 기계 코드 :
MSVC2019 생성 기계 코드 :
비교 결과는 Delphi에서 더 논리적으로 보입니다. (lon12> = 0) lon12가 + NAN 일 때 TRUE입니다. 이것은 버그가 MSVC2019 컴파일러에 있음을 의미합니까? 원래 C-Code의 테스트 데이터 세트가 오류가있는 것으로 간주해야합니까?
우선, 당신의 델파이 프로그램은 적어도 제가 쉽게 사용할 수있는 델파이 버전 인 XE7에서는 당신이 설명하는대로 동작하지 않습니다. 프로그램이 실행되면 잘못된 연산 부동 소수점 예외가 발생합니다. 실제로 부동 소수점 예외를 마스크했다고 가정하겠습니다.
업데이트 : XE7과 10.3 사이에 Delphi 32 비트 코드 생성이 XE7이 IA 부동 소수점 예외를 설정하는 이유를 설명 fcom
하는 fucom
것으로 전환 되었지만 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의 32 비트 (아래 주석에서보고 한대로 10.4.1)에서는 다음과 같이 출력됩니다.
진실 진실 진실 진실 그릇된 진실
XE7 및 10.3.3의 64 비트 (보고서는 10.4.1)에서 다음과 같이 출력됩니다.
그릇된 그릇된 그릇된 그릇된 그릇된 진실
64 비트 출력이 올 바릅니다. 두 변형 모두에 대한 32 비트 출력이 올바르지 않습니다. 이것은 IEEE754 NaN 값에 대해 false를 반환하는 모든 비교에 대한 근거는 무엇입니까? 를 참조하여 확인할 수 있습니다 .
연산자 ==, <=,> =, <,>를 사용한 모든 비교에서 하나 또는 두 값이 NaN 인 경우 다른 모든 값의 동작과는 반대로 false를 반환합니다.
32 비트 Delphi 코드의 경우이 버그를 해결하고 이러한 비교를 처리해야 할 때마다 특수한 경우 코드를 포함해야합니다. 물론, 당신이 10.4를 사용하고 있고 이미 문제가 해결되었다는 행복한 기회가 없다면.
이 기사는 인터넷에서 수집됩니다. 재 인쇄 할 때 출처를 알려주십시오.
침해가 발생한 경우 연락 주시기 바랍니다[email protected] 삭제
몇 마디 만하겠습니다