@Angew는 지적 의 !=
연산자는 양쪽에 동일한 유형을 필요로한다.
(float)i != i
결과적으로 RHS가 떠 다니는 것을 촉진하므로 (float)i != (float)i
.
g ++는 또한 무한 루프를 생성하지만 내부에서 작업을 최적화하지는 않습니다. 당신은 함께 INT-> 부동 소수점 변환 볼 수 있습니다 cvtsi2ss
및 수행 ucomiss xmm0,xmm0
비교 (float)i
자체. (이것은 C ++ 소스가 @Angew의 대답이 설명하는 것처럼 생각한 것을 의미하지 않는다는 첫 번째 단서였습니다.)
x != x
x
NaN 이기 때문에 "정렬되지 않은"경우에만 true 입니다. ( INFINITY
IEEE 수학에서 자신과 동일하지만 NaN은 그렇지 않습니다. NAN == NAN
거짓, NAN != NAN
참).
gcc7.4 및 이전 버전 jnp
은 루프 분기 ( https://godbolt.org/z/fyOhW1 ) 로 코드를 올바르게 최적화합니다 . 피연산자 x != x
가 NaN이 아닌 한 계속 루프를 수행합니다 . (gcc8 이상은 또한 je
루프의 브레이크 아웃을 확인 하여 NaN이 아닌 입력에 대해 항상 참이라는 사실을 기반으로 최적화에 실패합니다.) x86 FP는 순서가 지정되지 않은 상태에서 세트 PF를 비교합니다.
그리고 BTW는 clang의 최적화도 안전함 을 의미 합니다 . 단지 (float)i != (implicit conversion to float)i
동일한 것으로 CSE 해야 i -> float
하며 가능한 범위에서 NaN이 아님을 증명해야합니다 int
.
(이 루프가 signed-overflow UB에 도달한다는 점을 감안할 때, ud2
불법 명령을 포함하여 문자 그대로 원하는 asm을 방출 하거나 루프 본문이 실제로 무엇인지에 관계없이 빈 무한 루프 를 방출 할 수 있습니다.) 그러나 signed-overflow UB를 무시합니다. ,이 최적화는 여전히 100 % 합법적입니다.
GCC 는 부호있는 정수 오버플로를 잘 정의 하더라도-fwrapv
루프 본문을 최적화하지 못합니다 (2의 보수 순환으로 ). https://godbolt.org/z/t9A8t_
활성화 -fno-trapping-math
해도 도움이되지 않습니다. (GCC의 기본은 불행하게도 활성화
-ftrapping-math
에도 불구하고 그것의 GCC의 구현 / 버그를 파괴한다 .) 예외 가능성에 합리적인 아니에요 폭로와 INT-> 부동 소수점 변환이 때문에, (정확히 표현하기에 너무 큰 숫자) 예외 부정확 한 FP를 일으킬 수 있습니다 루프 본체를 최적화하십시오. ( 16777217
부정확 한 예외가 마스크 해제되면 float 로 변환 하면 관찰 가능한 부작용이 발생할 수 있기 때문 입니다.)
그러나 -O3 -fwrapv -fno-trapping-math
를 사용하면이를 빈 무한 루프로 컴파일하지 않는 것이 100 % 최적화를 놓쳤습니다. 가 없으면 #pragma STDC FENV_ACCESS ON
마스킹 된 FP 예외를 기록하는 고정 플래그의 상태는 코드의 관찰 가능한 부작용이 아닙니다. 아니오 int
-> float
변환은 NaN을 초래할 x != x
수 있으므로 사실 일 수 없습니다.
이러한 컴파일러는 모두 IEEE 754 단 정밀도 (binary32) float
및 32 비트 를 사용하는 C ++ 구현에 최적화되어 int
있습니다.
버그가 잡힌(int)(float)i != i
루프는 좁은 16 비트와 C ++ 구현에 UB있을 것입니다 int
및 / 또는 넓은 float
당신이 때렸어 때문에, 서명 정수 오버 플로우 UB A와 정확하게 표현할 수없는했던 첫 번째 정수에 도달하기 전에 float
.
그러나 다른 구현 정의 선택 세트 아래의 UB는 x86-64 System V ABI로 gcc 또는 clang과 같은 구현을 위해 컴파일 할 때 부정적인 결과를 초래하지 않습니다.
BTW, 에서 정의 된 FLT_RADIX
and 에서이 루프의 결과를 정적으로 계산할 수 있습니다 . 또는 적어도 이론적으로 는 Posit / unum과 같은 다른 종류의 실수 표현보다는 IEEE 플로트 모델에 실제로 적합 하다면 할 수 있습니다 .FLT_MANT_DIG
<climits>
float
ISO C ++ 표준이 float
동작 에 대해 얼마나 잘하고 있는지, 고정 너비 지수 및 유효 필드를 기반으로하지 않은 형식이 표준을 준수하는지 여부는 확실하지 않습니다.
댓글에서 :
@geza 결과 번호를 듣고 싶습니다!
@nada : 16777216입니다
이 루프를 인쇄 / 반환 할 수 있다고 주장 16777216
하십니까?
업데이트 : 해당 댓글이 삭제되었으므로 삭제되지 않은 것 같습니다. 아마도 OP는 float
32 비트로 정확하게 표현할 수없는 첫 번째 정수 앞의 인용 부호 일 것 float
입니다. https://en.wikipedia.org/wiki/Single-precision_floating-point_format#Precision_limits_on_integer_values 즉,이 버그가있는 코드로 확인하려는 내용.
버그 수정 버전은 물론 16777217
그 이전의 값보다는 정확하게 표현할 수 없는 첫 번째 정수인 print 합니다.
(높은 부동 소수점 값은 모두 정확한 정수이지만 유효 폭보다 높은 지수 값에 대해 2, 4, 8 등의 배수입니다. 더 높은 정수 값이 많이 표시 될 수 있지만 마지막 자리에는 1 단위가 있습니다. (유효 값의)는 1보다 크므로 연속 된 정수가 아닙니다. 가장 큰 유한 float
은 2 ^ 128 바로 아래이며 짝수에 비해 너무 큽니다 int64_t
.)
컴파일러가 원래 루프를 종료하고 인쇄하면 컴파일러 버그입니다.