이것은 버그 일뿐입니다.gcc 정밀 버그?
double sum_1 = 4.0 + 6.3;
assert(sum_1 == 4.0 + 6.3);
double t1 = 4.0, t2 = 6.3;
double sum_2 = t1 + t2;
assert(sum_2 == t1 + t2);
하지 않으면 버그, 이유 : 두 번째 실패하면서 첫 번째 어설 통과?
이것은 버그 일뿐입니다.gcc 정밀 버그?
double sum_1 = 4.0 + 6.3;
assert(sum_1 == 4.0 + 6.3);
double t1 = 4.0, t2 = 6.3;
double sum_2 = t1 + t2;
assert(sum_2 == t1 + t2);
하지 않으면 버그, 이유 : 두 번째 실패하면서 첫 번째 어설 통과?
이것은 나에게도 물린 것입니다.
예, 반올림 오류로 인해 부동 소수점 수를 비교해서는 안되며, 아마 알고있을 것입니다.
하지만이 경우에는 t1+t2
을 계산하고 다시 계산합니다. 확실하게 동일한 결과를 산출해야하는?
여기에 설명되어 있습니다. 이걸 x86 CPU에서 돌리고있을거야, 맞습니까? x86 FPU는 내부 레지스터에 80 비트를 사용하지만 메모리의 값은 64 비트 2 배로 저장됩니다.
따라서 t1+t2
은 먼저 정밀도가 80 비트로 계산 된 다음 정밀도가 64 비트 인 sum_2
의 메모리에 저장되고 일부 반올림이 발생합니다. 어설 션을 위해 부동 소수점 레지스터에 다시로드되고 t1+t2
이 80 비트 정밀도로 다시 계산됩니다. 이제 이전에 64 비트 부동 소수점 값으로 반올림 한 sum_2
을 t1+t2
으로 더 높은 정밀도 (80 비트)로 계산 했으므로 값이 정확히 일치하지 않습니다.
왜 첫 번째 테스트는 통과합니까? 이 경우 컴파일러는 컴파일시에 4.0+6.3
을 평가하고이를 할당 및 어설 션 모두에 대해 64 비트 수량으로 저장합니다.그래서 동일한 값이 비교되고 assert가 성공합니다.
두 번째 편집 다음은 주석이있는 코드 (GCC, 86)의 두 번째 부분에 대해 생성 된 어셈블리 코드의 - 거의가 위에서 설명한 시나리오 다음과
// t1 = 4.0
fldl LC3
fstpl -16(%ebp)
// t2 = 6.3
fldl LC4
fstpl -24(%ebp)
// sum_2 = t1+t2
fldl -16(%ebp)
faddl -24(%ebp)
fstpl -32(%ebp)
// Compute t1+t2 again
fldl -16(%ebp)
faddl -24(%ebp)
// Load sum_2 from memory and compare
fldl -32(%ebp)
fxch %st(1)
fucompp
재미있는 측면 참고 :이 최적화없이 컴파일되었습니다. -O3
으로 컴파일 될 때 컴파일러는 코드 모두 을 최적화합니다.
나는 그것이 심지어 그것이라고 생각하지 않는다. 것은'4.0 + 6.3'은 컴파일러에 의해 10.3으로 상수 형으로 접혀진 표현식입니다. 따라서 첫 번째 주장은'assert (10.3 == 10.3)'과 동등해진다. 두 번째 테스트에서는 실제로는 4.0을, 두 번에 넣고, 6.3을 사용하고, 두 배로 (작은 정밀도를 잃어 버림) 두 개를 합치고, 더한 값을 * 상수 10.3과 비교합니다. 2^-70 정도 다르다. :) – hobbs
컴파일러가 assert 문에서 t1 + t2를 10.3으로 최적화 할 수 있다면 왜 sum_2에 대한 할당에서 동일한 최적화를 수행 할 수 없습니까? –
목록을 보면 나에게있어. :) 내 부분에 가난한 추측, 나는 생각한다. – hobbs
부동 소수점 숫자를 비교하고 있습니다. 그렇게하지 않으면, 부동 소수점 숫자는 경우에 따라 고유 한 정밀도 오류가 있습니다. 대신에 두 값의 차이의 절대 값을 취하여 값이 작은 수 (ε)보다 작다고 주장하십시오.
void CompareFloats(double d1, double d2, double epsilon)
{
assert(abs(d1 - d2) < epsilon);
}
이것은 컴파일러와 관련이 없으며 부동 소수점 숫자가 구현되는 방식과 관련이 있습니다. 여기에 IEEE 사양은 다음과 같습니다 이중 정밀도 숫자의
http://www.eecs.berkeley.edu/~wkahan/ieee754status/IEEE754.PDF
에드 (Ed)가 말하는 정밀도 오류는 부동 소수점 연산의 기능입니다. 버그는 아니며 gcc 작성자의 부주의 한 구현이 아니라 컴퓨터가 분수로 산술을 수행하는 방식입니다. Google은 '모든 컴퓨터 과학자가 부동 소수점 연산에 대해 알아야하는 것'에 대해 중점적으로 강의하고 있습니다. –
예, 사양에 대한 링크를 추가했습니다. –
Google에도이 "버그"가 있습니다. http://www.google.com/search?hl=ko&source=hp&q=99999999999999++-999999999999998&btnG=Google+Search&cts=1251328513752&aq=f&oq=&aqi= – llamaoo7
비교는 본질적으로 정확하지 않습니다. 예를 들어 0.0 == 0.0
이 을 반환하는 경우가 있습니다. 이는 FPU가 숫자를 저장하고 추적하는 방식 때문입니다.
Wikipedia says : 평등에 대한
테스트는 문제가있다. 수학적으로 동일한 두 계산 열은 서로 다른 부동 소수점 값을 생성 할 수 있습니다.
델타를 사용하여 정확한 값이 아닌 비교를 허용해야합니다.
두 경우 중 하나에서 64 비트 더블을 80 비트 내부 레지스터와 비교할 수 있습니다. GCC에서 두 가지 경우에 대해 내놓는 어셈블리 지침을 살펴 보는 것이 계몽적일 수 있습니다 ...
내 인텔 코어 2 듀오에서 문제가 중복되어 어셈블리 코드를 살펴 보았습니다. 여기에 무슨 일이 일어나고 있는지의 : 컴파일러가 t1 + t2
을 평가할 때,
round the 80-bit sum to a 64-bit number and store it
는 그 다음 ==
비교 64 비트 합계 80 비트의 합을 비교하지
load t1 into an 80-bit register
load t2 into an 80-bit register
compute the 80-bit sum
가 저장 sum_2
으로 수행, 왜냐하면 주로 부분적인 부분 0.3은 2 진 부동 소수점 숫자를 사용하여 정확하게 표현 될 수 없기 때문에 두 개의 다른 길이로 잘린 '반복 십진수'(실제로 반복 바이너리)를 비교합니다.
gcc -O1
또는 gcc -O2
으로 컴파일하면 gcc가 컴파일 할 때 잘못된 산술을 수행하고 문제가 사라집니다. 어쩌면 이것은 표준에 따르면 괜찮지 만 gcc가 내가 가장 좋아하는 컴파일러가 아닌 이유 중 하나 일뿐입니다.
P. ==
은 80 비트 합계와 64 비트 합계를 비교한다고 말하면 물론 실제로 64 비트 합계의 확장 버전을 비교한다는 의미입니다. 당신은 부동 소수점의 놀라운 세계에
sum_2 = round(extend(t1) + extend(t2))
시작으로
extend(sum_2) == extend(t1) + extend(t2)
및
sum_2 = t1 + t2
가 해결에
sum_2 == t1 + t2
가 해결 생각을 잘 할 수있다!
당신이 일반적으로
예를 들어if (abs(x) != 0 || abs(y) != 0)
rel_diff (x, y) = abs((x - y)/max(abs(x),abs(y))
else
rel_diff(x,y) = max(abs(x),abs(y))
으로 정의된다 상대적인 차이를 측정 할 친밀감에 대한 부동 소수점 숫자를 비교, 아이디어는 선도의 수를 측정하는 것입니다
rel_diff(1.12345, 1.12367) = 0.000195787019
rel_diff(112345.0, 112367.0) = 0.000195787019
rel_diff(112345E100, 112367E100) = 0.000195787019
숫자가 공통으로 가지고있는 유효 숫자; 0.000195787019의 -log10을 가져 가면 3.70821611이되는데, 이것은 모든 예제가 공통으로 가지고있는 기본 10 자리 숫자에 해당합니다.
두 개의 부동 소수점 숫자는 기계 엡실론 사용되는 부동 소수점 하드웨어의 가수에서 개최 할 수있는 가장 작은 수입니다 당신이
if (rel_diff(x,y) < error_factor * machine_epsilon()) then
print "equal\n";
처럼 뭔가해야 동일한 경우 결정해야합니다. 대부분의 컴퓨터 언어에는이 값을 얻기위한 함수 호출이 있습니다. error_factor는 숫자 x와 y의 계산에서 반올림 오류 (및 기타)에 의해 소비 될 것으로 생각되는 유효 자릿수에 기초해야합니다. 예를 들어, x와 y가 약 1000 개의 합계의 결과이고 합계되는 숫자의 경계를 알지 못하는 경우 error_factor를 약 100으로 설정합니다.
링크로 추가하려고 시도했지만 ' 이 이후 t 내 첫 번째 게시물입니다 :
이 "문제는"이러한 옵션을 사용하여 "고정"할 수있다 : I되면
http://www.network-theory.co.uk/docs/gccintro/gccintro_70.html
:이 페이지에 설명 된대로
-msse2 -mfpmath =
SSE를 이 옵션을 사용하면 양쪽 모두 어설 션을 통과했습니다.
다음과 같이 볼 수도 있습니다. - [http://stackoverflow.com/questions/21265/comparing-ieee-floats-and-doubles-for-equality](http://stackoverflow.com/questions/21265/) 비교 ieee-floats-and-doubles-for-equality) - [http://stackoverflow.com/questions/17333/most-effective-way-for-float-and-double-comparison](http://stackoverflow .com/questions/17333/most-effective-way-for-float-and-double-comparison) - [http://stackoverflow.com/questions/713763/strange-results-with-floating-point-comparison] – pingw33n