내가 사용하는 것을 알고 ==
부동 소수점 변수의 평등을 확인하는 것은 좋은 방법이 아닙니다. 그러나 나는 다음 진술로 그것을 알고 싶습니다.
float x = ...
float y = x;
assert(y == x)
이후 y
에서 복사 x
, 주장은 사실 일 것이다?
-m32
) 를 컴파일 하거나 GCC에 x87 FPU ( -mfpmath=387
) 를 사용하도록 지시 하여 재현 할 수 있습니다 .
내가 사용하는 것을 알고 ==
부동 소수점 변수의 평등을 확인하는 것은 좋은 방법이 아닙니다. 그러나 나는 다음 진술로 그것을 알고 싶습니다.
float x = ...
float y = x;
assert(y == x)
이후 y
에서 복사 x
, 주장은 사실 일 것이다?
-m32
) 를 컴파일 하거나 GCC에 x87 FPU ( -mfpmath=387
) 를 사용하도록 지시 하여 재현 할 수 있습니다 .
답변:
assert(NaN==NaN);
kmdreko가 지적한 경우 외에도 80 비트 부동 소수점이 메모리에 임시로 저장된 후 나중에 레지스터 내부에 저장된 값과 비교되는 x87-math가있는 상황이 발생할 수 있습니다.
가능한 최소 예제 -O2 -m32
. 다음 과 같이 컴파일하면 gcc9.2에서 실패합니다 .
#include <cassert>
int main(int argc, char**){
float x = 1.f/(argc+2);
volatile float y = x;
assert(x==y);
}
Godbolt 데모 : https://godbolt.org/z/X-Xt4R
는 volatile
충분한 레지스터 압력을 만들기 위해 관리하는 경우 아마 한 것으로, 생략 할 수 y
저장하고 메모리에서 다시로드 (그러나 컴파일러만큼, 모두 함께 비교를 생략하지 혼동).
GCC FAQ 참조를 참조하십시오.
float
표준 정밀도와 추가 정밀도를 비교할 때 추가 비트를 고려하는 것이 이상하게 보입니다 .
-ffloat-store
이것을 언급 할 가치가있는 것으로 보입니다.
에 대한 비교 가 항상 거짓 이므로 (예, 짝수 ) x
is NaN
인 경우 사실 NaN
이 아닙니다NaN == NaN
. 다른 모든 경우 (정상 값, 비정규 값, 무한대, 0)의 경우이 어설 션이 적용됩니다.
부동 소수점 을 피하기 ==
위한 조언은 부동 소수점 숫자가 산술 표현식에서 사용될 때 많은 결과를 정확하게 표현할 수 없기 때문에 계산에 적용됩니다 . 할당은 계산이 아니며 할당이 원래와 다른 값을 산출 할 이유가 없습니다.
표준을 준수하는 경우 확장 정밀 평가는 문제가되지 않아야합니다. <cfloat>
C 에서 상속 된 것 [5.2.4.2.2.8] ( 강조 광산 ) :
할당 및 캐스트 (모든 추가 범위 및 정밀도를 제거함)를 제외하고 , 일반 피연산자 및 부동 상수에 종속 된 부동 피연산자 및 값이있는 연산의 값은 범위 및 정밀도가 필요한 것보다 큰 형식으로 평가됩니다. 유형.
그러나 주석에서 지적했듯이 특정 컴파일러, 빌드 옵션 및 대상 이 있는 일부 경우에는 역설적으로 거짓이 될 수 있습니다.
x
첫 번째 행의 레지스터에서 계산되는 경우 a의 최소값보다 더 높은 정밀도를 유지합니다 float
. 은 y = x
단지 유지, 메모리에있을 수 있습니다 float
정밀도를. 그런 다음 레지스터에 대한 메모리로 다른 정밀도로 평등 테스트를 수행하므로 보장 할 수 없습니다.
x+pow(b,2)==x+pow(a,3)
다를 수 auto one=x+pow(b,2); auto two=y+pow(a,3); one==two
(intermediste 값 FPU 80ish 비트에있는 동안, 하나 / 두 RAM의 64 개 비트 값을 인 경우) 다른 것보다 더 정밀하여 비교할 수 있기 때문에 하나. 따라서 과제는 때때로 무언가를 할 수 있습니다.
gcc -ffloat-store
엄격한 준수 를 위해 이를 강제 할 수 있습니다 . 그러나이 질문은 x=y; x==y;
중간에 var에 아무것도하지 않고 관한 것입니다. float에 맞게 이미 반올림 된 경우 y
double 또는 long double 및 back으로 변환해도 값이 변경되지 않습니다. ...
예, y
확실히의 값에 걸릴 것입니다 x
:
[expr.ass]/2
: 단순 할당 (=)에서 왼쪽 피연산자가 참조하는 객체는 해당 값을 오른쪽 피연산자의 결과로 대체하여 수정됩니다 ([defns.access]).
다른 값을 지정할 여지가 없습니다.
(다른 사람들은 동등성 비교 ==
가 그럼에도 불구하고 false
NaN 값 에 대해 평가 될 것이라고 이미 지적했다 .)
부동 소수점의 일반적인 문제 ==
는 자신 이 생각하는 가치를 쉽게 갖지 못한다 는 것입니다. 여기서 우리는 두 값이 모두 동일하다는 것을 알고 있습니다.
[expr]
. 링크를 무시하고 인용에 초점을 맞추려면 C.5.3 과 같이 "값"또는 "결과"라는 용어를 사용하지 않는 것처럼 혼동 될 수 있습니다. 일반적인 영어 상황에서 "결과"를 한 번 사용하십시오. 아마도 표준이 구별되는 부분을 더 명확하게 설명하고 이러한 일에 대한 명확한 인용을 제공 할 수있을 것입니다. 감사!
예, 모든 경우 (NaN 및 x87 문제는 무시) 이것이 사실입니다.
당신이 memcmp
그들에 대해하면 NaN과 sNaN을 비교하면서 평등을 테스트 할 수 있습니다. 또한 컴파일러는 변수의 주소를 사용하여 값을 float
80 비트 대신 32 비트로 강제 변환해야합니다 . 이것은 x87 문제를 제거합니다. 여기서 두 번째 주장은 ==
NaN을 참으로 비교하지 않을 것이라는 것을 보여 주려는 의도 는 아닙니다.
#include <cmath>
#include <cassert>
#include <cstring>
int main(void)
{
float x = std::nan("");
float y = x;
assert(!std::memcmp(&y, &x, sizeof(float)));
assert(y == x);
return 0;
}
NaN의 내부 표현이 다르면 (즉, 가수가 다름) memcmp
사실과 비교되지 않습니다.
일반적인 경우에는 true로 평가됩니다. (또는 주장 진술은 아무것도하지 않을 것입니다)
편집 :
'일반적인 경우'라는 말은 다른 사용자가 지적한 것처럼 NaN 값 및 80x87 부동 소수점 단위와 같은 앞에서 언급 한 시나리오를 제외한다는 의미입니다.
오늘날의 맥락에서 8087 칩이 더 이상 사용되지 않기 때문에 문제는 다소 분리되어 있으며 사용 된 부동 소수점 아키텍처의 현재 상태에 적용 할 수 있습니다 .NaN을 제외한 모든 경우에 해당됩니다.
(8087에 대한 참조 - https://home.deec.uc.pt/~jlobo/tc/artofasm/ch14/ch143.htm )
좋은 예를 재현 해 준 @chtz와 NaN을 언급 한 @kmdreko의 쿠도스 (Kodos) – 이전에는 그것들에 대해 몰랐습니다!
x
동안 부동 소수점 레지스터에있을 수 있다고 생각했습니다 y
. 메모리가 레지스터보다 정밀도가 떨어지면 비교에 실패 할 수 있습니다.
float
추가 정밀도가없는 값 이어야합니다 .
int a=1; int b=a; assert( a==b );
주장을 던질 수 있다고 생각하면 올바르게 작동하는 컴파일러와 관련 하여이 질문에 대답하는 것이 합리적이라고 생각합니다 (일부 컴파일러의 일부 버전은 / -알려져서 잘못되었습니다). 실제로 어떤 이유로 컴파일러가 레지스터 저장 할당의 결과에서 추가 정밀도를 제거하지 않으면 해당 값을 사용 하기 전에 그렇게해야합니다 .
예, NaN 인 경우를 제외하고 항상 True를 반환 합니다. 변수 값이 NaN 이면 항상 False를 반환합니다 .