플로트 첨가가 두 배로 촉진 되었습니까?
-
12-09-2019 - |
문제
오늘 아침에 작은 WTF 순간이있었습니다. wtf는 다음과 같이 요약 할 수 있습니다.
float x = 0.2f;
float y = 0.1f;
float z = x + y;
assert(z == x + y); //This assert is triggered! (Atleast with visual studio 2008)
그 이유는 그 표현이었습니다 x + y
두 배로 홍보되고 절단 된 버전과 비교됩니다. z
. (내가 변하면 z
에게 double
인식이 트리거되지 않습니다).
정확한 이유로 결과를 단일 정밀도로 변환하기 전에 모든 부동 소수점 산술을 이중 정밀하게 수행하는 것이 합리적이라는 것을 알 수 있습니다. 나는 표준에서 다음 단락을 발견했습니다 (나는 이미 알고 있었지만이 맥락에서는 그렇지 않은 것 같아요).
4.6.1."유형의 RValue float
유형의 rvalue로 변환 할 수 있습니다 double
. 가치는 변하지 않습니다. "
내 질문은입니다 x + y
두 배로 승진하거나 컴파일러의 재량에 따라 보장 되나요?
업데이트: 많은 사람들이 사용해서는 안된다고 주장했기 때문에 ==
플로팅 포인트의 경우, 제가 작업하는 특정 경우에 정확한 비교가 정당하다고 진술하고 싶었습니다.
부동 소수점 비교 ~이다 까다 롭습니다. 여기 흥미로운 점이 있습니다 링크 언급되지 않은 주제에 대해.
해결책
당신은 일반적으로 그것을 가정 할 수 없습니다 ==
부동 소수점 유형에 대해 예상대로 작동합니다. 둥근 값을 비교하거나 같은 구조물을 비교하십시오 abs(a-b) < tolerance
대신에.
프로모션은 전적으로 컴파일러의 재량에 달려 있으며 대상 하드웨어, 최적화 수준 등에 따라 다릅니다).
이 특정 사례에서 일어나고있는 것은 값이 메모리보다 더 높은 정밀도로 FPU 레지스터에 저장된다는 것이 거의 확실합니다. 일반적으로 현대 FPU 하드웨어는 프로그래머가 요청한 내부적으로 정밀도로 두 배 이상의 정밀도로 작동하며 컴파일러가 코드를 생성합니다. 값이 메모리에 저장 될 때 적절한 변환을 만들기 위해; 최적화되지 않은 빌드에서 결과 x+y
비교가 이루어진 시점에 여전히 레지스터에 있지만 z
추억에 저장되어 뒤로 가져와 정밀도를 떠 다니기 위해 잘 렸습니다.
다른 팁
그만큼 다음 표준 C ++ 0x의 작업 초안 섹션 5 지점 11은 말합니다
부유 식 피연산자의 값과 부동 표현의 결과는 유형에 의해 요구되는 것보다 더 큰 정밀도와 범위로 표현 될 수있다. 유형이 변경되지 않습니다
컴파일러의 재량에 따라.
GCC 4.3.2를 사용하면 주장이 있습니다 ~ 아니다 트리거되고 실제로 RValue가 돌아 왔습니다 x + y
a float
, a보다는 double
.
따라서 컴파일러에 달려 있습니다. 그렇기 때문에 두 개의 부동 소수점 값 사이의 정확한 평등에 의존하는 것이 결코 현명하지 않습니다.
C ++ FAQ Lite는 주제에 대한 추가 논의를 가지고 있습니다.
이진 변환에 대한 플로트 번호는 정확한 정밀도를 제공하지 않기 때문에 문제입니다.
그리고 내부 sizeof(float)
바이트는 플로트 번호의 정확한 값을 호소 할 수 없으며 산술 작동은 근사치로 이어질 수 있으므로 평등이 실패합니다.
아래를 참조하십시오
float x = 0.25f; //both fits within 4 bytes with precision
float y = 0.50f;
float z = x + y;
assert(z == x + y); // it would work fine and no assert
나는 그것이 컴파일러의 재량에있을 것이라고 생각하지만, 그것이 당신의 생각이라면 항상 캐스트로 강제로 강요 할 수 있습니까?
플로트를 직접 비교하지 않는 또 다른 이유.
if (fabs(result - expectedResult) < 0.00001)