(A + B + C) ≠ (A + C + B) 및 컴파일러 재정렬
두 개의 32 비트 정수를 추가하면 정수 오버 플로우가 발생할 수 있습니다.
uint64_t u64_z = u32_x + u32_y;
32 비트 정수 중 하나가 먼저 캐스트되거나 64 비트 정수에 추가되는 경우이 오버 플로우를 피할 수 있습니다.
uint64_t u64_z = u32_x + u64_a + u32_y;
그러나 컴파일러가 추가를 재정렬하기로 결정한 경우 :
uint64_t u64_z = u32_x + u32_y + u64_a;
정수 오버플로가 여전히 발생할 수 있습니다.
컴파일러는 이러한 재정렬을 허용합니까, 아니면 결과 불일치를 확인하고 표현식 순서를 그대로 유지하도록 신뢰할 수 있습니까?
옵티마이 저가 이러한 재정렬을 수행하는 경우 여전히 C 스펙에 바인딩되므로 이러한 재정렬은 다음과 같습니다.
uint64_t u64_z = (uint64_t)u32_x + (uint64_t)u32_y + u64_a;
이론적 해석:
우리는 시작합니다
uint64_t u64_z = u32_x + u64_a + u32_y;
추가는 왼쪽에서 오른쪽으로 수행됩니다.
정수 프로모션은 원래 표현에서 처음으로뿐만 아니라, 그 상태를 규칙 u32_x
으로 승격 uint64_t
. 두 번째 추가에서는으로 u32_y
도 승격됩니다 uint64_t
.
따라서 C 사양을 준수하려면 옵티마이 저가 부호없는 64 비트 값을 승격 u32_x
하고 u32_y
64 비트로 늘려야합니다. 캐스트를 추가하는 것과 같습니다. (실제 최적화는 C 수준에서 수행되지 않지만 C 표기법을 사용합니다. 왜냐하면 우리가 이해하는 표기법이기 때문입니다.)
컴파일러는 as 규칙 에 따라서 만 순서를 다시 지정할 수 있습니다. 즉, 재정렬이 항상 지정된 순서와 동일한 결과를 제공하면 허용됩니다. 그렇지 않으면 (예와 같이) 아닙니다.
예를 들어 다음과 같은식이 주어지면
i32big1 - i32big2 + i32small
크지 만 비슷하다고 알려진 두 개의 값을 빼고 신중하게 구성되어 다른 작은 값 을 추가하면 (오버플로를 피할 수 있음) 컴파일러는 다음과 같이 재정렬하도록 선택할 수 있습니다.
(i32small - i32big2) + i32big1
그리고 대상 플랫폼이 문제를 방지하기 위해 랩 어라운드와 함께 2 개의 보수 산술을 사용하고 있다는 사실에 의존합니다. (컴파일러가 레지스터를 위해 눌려지고 i32small
이미 레지스터에 있는 경우 이러한 순서 변경이 합리적 일 수 있습니다 ).
C, C ++ 및 Objective-C에는 "as as"규칙이 있습니다. 호환되는 프로그램이 차이점을 알 수없는 한 컴파일러는 원하는대로 수행 할 수 있습니다.
이러한 언어에서 a + b + c는 (a + b) + c와 동일하게 정의됩니다. 이것과 예를 들어 a + (b + c)의 차이점을 알 수 있으면 컴파일러는 순서를 변경할 수 없습니다. 차이점을 알 수 없다면 컴파일러는 순서를 자유롭게 바꿀 수 있지만 차이점을 알 수 없기 때문에 괜찮습니다.
귀하의 예에서 b = 64 비트, a 및 c 32 비트를 사용하면 컴파일러는 차이를 알 수 없기 때문에 (b + a) + c 또는 심지어 (b + c) + a를 평가할 수 있지만 차이를 알 수 있기 때문에 (a + c) + b는 아닙니다.
다시 말해, 컴파일러는 코드와 다른 동작을하는 어떤 것도 할 수 없습니다. 생성한다고 생각하거나 생성해야한다고 생각하는 코드를 생성 할 필요는 없지만 코드 는 결과를 정확하게 제공합니다.
표준 에서 인용 :
[참고 : 연산자는 연산자가 실제로 연관되거나 정식 인 경우에만 일반적인 수학 규칙에 따라 다시 그룹화 할 수 있습니다 .7 예를 들어 다음 조각 int a, b;
/∗ ... ∗/ a = a + 32760 + b + 5;
표현 문은 다음과 정확히 동일하게 동작합니다
a = (((a + 32760) + b) + 5);
이러한 연산자의 연관성과 우선 순위로 인해 따라서 합계 (a + 32760)의 결과가 다음에 b에 추가되고 그 결과가 5에 추가되어 a에 지정된 값이됩니다. 오버플로가 예외를 생성하고 int로 표시 할 수있는 값 범위가 [-32768, + 32767] 인 시스템에서 구현은이 표현식을 다음과 같이 다시 작성할 수 없습니다.
a = ((a + b) + 32765);
a와 b의 값이 각각 -32754와 -15 인 경우 합 a + b는 예외를 생성하지만 원래 표현식은 그렇지 않습니다. 식을 다음과 같이 다시 쓸 수도 없습니다.
a = ((a + 32765) + b);
또는
a = (a + (b + 32765));
a와 b의 값은 각각 4와 -8 또는 -17과 12 일 수 있기 때문에 오버플로가 예외를 생성하지 않고 오버플로의 결과를 되돌릴 수있는 시스템에서는 위의 표현 문이 동일한 결과가 발생하기 때문에 위의 방법 중 하나로 구현에 의해 다시 작성됩니다. — 끝 참고]
컴파일러는 이러한 재정렬을 허용합니까, 아니면 결과 불일치를 확인하고 표현식 순서를 그대로 유지하도록 신뢰할 수 있습니까?
컴파일러는 동일한 결과를 제공하는 경우에만 순서를 바꿀 수 있습니다. 여기에서 관찰 한 것처럼 그렇지 않습니다.
std::common_type
추가 하기 전에 모든 인수를 승격시키는 함수 템플릿을 작성하는 것이 가능 합니다. 인수 순서 나 수동 캐스팅에 의존하지 않지만 안전합니다.
의 비트 너비에 따라 다릅니다 unsigned/int
.
아래 2는 동일하지 않습니다 ( unsigned <= 32
비트 일 때 ). u32_x + u32_y
0이됩니다.
u64_a = 0; u32_x = 1; u32_y = 0xFFFFFFFF;
uint64_t u64_z = u32_x + u64_a + u32_y;
uint64_t u64_z = u32_x + u32_y + u64_a; // u32_x + u32_y carry does not add to sum.
그것들은 동일하다 ( unsigned >= 34
비트 일 때 ). 정수 승격으로 인해 u32_x + u32_y
64 비트 수학에서 추가가 발생했습니다. 순서는 관련이 없습니다.
UB ( unsigned == 33
비트 일 때 )입니다. 정수 승격으로 부호있는 33 비트 수학에서 추가가 발생했으며 부호있는 오버 플로우는 UB입니다.
컴파일러는 이러한 재정렬을 할 수 있습니까?
(32 비트 연산) : 다시 주문 네,하지만 같은 결과는 그렇게하지, 발생해야 한다는 재정렬의 영업 이익은 제안한다. 아래는 동일합니다
// Same
u32_x + u64_a + u32_y;
u64_a + u32_x + u32_y;
u32_x + (uint64_t) u32_y + u64_a;
...
// Same as each other below, but not the same as the 3 above.
uint64_t u64_z = u32_x + u32_y + u64_a;
uint64_t u64_z = u64_a + (u32_x + u32_y);
... 결과 불일치를 확인하고 표현 순서를 그대로 유지한다고 믿을 수 있습니까?
Trust yes, but OP's coding goal is not crystal clear. Should u32_x + u32_y
carry contribute? If OP wants that contribution, code should be
uint64_t u64_z = u64_a + u32_x + u32_y;
uint64_t u64_z = u32_x + u64_a + u32_y;
uint64_t u64_z = u32_x + (u32_y + u64_a);
But not
uint64_t u64_z = u32_x + u32_y + u64_a;
참고URL : https://stackoverflow.com/questions/38563707/a-b-c-%e2%89%a0-a-c-b-and-compiler-reordering
'programing tip' 카테고리의 다른 글
java.util.Date와 XMLGregorianCalendar 간의 간단한 변환 (0) | 2020.08.02 |
---|---|
"스텁"이란 무엇입니까? (0) | 2020.08.02 |
Java의 정적 할당-힙, 스택 및 영구 생성 (0) | 2020.08.02 |
KeyValuePair VS DictionaryEntry (0) | 2020.08.02 |
JSON.parse 예기치 않은 문자 오류 (0) | 2020.07.30 |