왜 이것이 정의되지 않은 동작입니까?
inline bool divisible15(unsigned int x)
{
//286331153 = (2^32 - 1) / 15
//4008636143 = (2^32) - 286331153
return x * 4008636143 <= 286331153;
}
VS2008 컴파일러를 사용하는 머신에서는 완벽하게 동작했지만 여기서는 전혀 동작하지 않습니다.
컴파일러마다 다른 결과가 나오는 이유를 아시는 분? unsigned
overflow는 정의되지 않은 동작이 아닙니다.
주의: 테스트 결과, 나눗셈의 나머지를 15까지 취득하는 것보다 빠른 것이 확인되었습니다(단, 모든 컴파일러에서는 제외).
정의되지 않은 동작은 아닙니다.C89와 C99 사이의 C 언어 표준이 바뀌었을 뿐입니다.
C89에서, 4008636143과 같은 정수 상수는 다음과 같습니다.int
또는long int
하지만 꼭 들어맞는다.unsigned int
서명되지 않았지만 C99에서는 다음 중 하나입니다.long int
또는long long int
(값을 유지할 수 있는 최소 크기에 따라 다름)그 결과 모든 표현은 64비트를 사용하여 평가되므로 오답이 발생합니다.
Visual Studio는 C89 컴파일러이므로 C89 동작이 발생하지만 이 IDEONE 링크는 C99 모드로 컴파일 중입니다.
이는 GCC를 사용하여 컴파일하면 더욱 명확해집니다.-Wall
:
test.c: In function ‘divisible15’:
test.c:8:3: warning: this decimal constant is unsigned only in ISO C90
C89 3 3.1.3.2부터:
정수 상수의 유형은 해당 값을 나타낼 수 있는 목록의 첫 번째 유형입니다.고정되지 않은 10진수: int, long int, 부호 없는 long int, 고정되지 않은 8진수 또는 16진수: int, 부호 없는 int, 부호 없는 long int; [...
C99 § 6.4.4.1/5-6:
5) 정수 상수의 유형은 해당 정수 상수의 값을 나타낼 수 있는 목록의 첫 번째 유형이다.
Suffix | Decimal Constant | Octal or Hexadecimal Constant -------+------------------+------------------------------ none | int | int | long int | unsigned int | long long int | long int | | unsigned long int | | long long int | | unsigned long long int -------+------------------+------------------------------ [...]
6) 정수형이 목록 중 어떤 형식으로도 나타낼 수 없는 경우에는 확장 정수형이 그 값을 나타낼 수 있는 경우에는 확장 정수형을 가질 수 있다.상수 목록에 있는 모든 유형이 서명된 경우 확장 정수 유형이 서명됩니다. [...]
완전성을 위해 C++03은 정수 정수가 너무 커서 C+03에 맞지 않는 경우 실제로 정의되지 않은 동작을 가집니다.long int
C++03 § 2.13.1/2부터:
정수 리터럴의 유형은 형식, 값 및 접미사에 따라 달라집니다.10진수이며 접미사가 없는 경우 값을 나타낼 수 있는 첫 번째 유형은 다음과 같습니다.
int
,long int
; 값을 a로 나타낼 수 없는 경우long int
동작은 정의되어 있지 않습니다.10월 16진수이고 접미사가 없는 경우, 이 값은 μ μ μ μ μ μ μ μ μ μ μ μ μ μ μ μ μint
, , ,unsigned int
, , ,long int
, , ,unsigned long int
. . [...]. [...]
C++11 의 동작은, C99 와 같습니다.C++11 의 2.14.2/3 를 참조해 주세요.
c89, C+03 정, C+03 μ+3 μ μ μ μ μ μ μ μ μ μ μ μ μ μ μu
as ~하듯이4008636143u
....
Since you are using 사용하고 계시기 때문에int
상수, 컴파일러는 "사용" 오버플로이 정의되지 않을 수 있습니다.상수, 컴파일러는 코드를 단축하기 위해 정의되지 않은 오버플로를 "사용"할 수 있습니다.아래의 U를 수정하면 "작업"입니다.아래와 같이 U로 수정하면 "작동"합니다.
inline bool divisible15(unsigned int x)
{
//286331153 = (2^32 - 1) / 15
//4008636143 = (2^32) - 286331153
return x * 4008636143u <= 286331153u;
}
테스트 대상:
#include <iostream>
inline bool divisible15a(unsigned int x)
{
//286331153 = (2^32 - 1) / 15
//4008636143 = (2^32) - 286331153
// return x * 4008636143 <= 286331153;
return x * 4008636143u <= 286331153;
}
inline bool divisible15b(unsigned int x)
{
//286331153 = (2^32 - 1) / 15
//4008636143 = (2^32) - 286331153
// return x * 4008636143 <= 286331153;
return x * 4008636143 <= 286331153;
}
int main()
{
for(unsigned int i = 0; i < 100; i++)
{
if (divisible15a(i))
{
std::cout << "a:" << i << std::endl;
}
if (divisible15b(i))
{
std::cout << "b:" << i << std::endl;
}
}
}
출력:
a:0
b:0
a:15
a:30
a:45
a:60
a:75
a:90
코드:
_Z12divisible15aj:
.LFB1192:
pushq %rbp
movq %rsp, %rbp
movl %edi, -4(%rbp)
movl -4(%rbp), %eax
imull $-286331153, %eax, %eax
cmpl $286331153, %eax
setbe %al
popq %rbp
ret
_Z12divisible15bj:
.LFB1193:
pushq %rbp
movq %rsp, %rbp
movl %edi, -4(%rbp)
movl -4(%rbp), %edx
movl $4008636143, %eax
imulq %rdx, %rax
cmpq $286331153, %rax
setle %al
popq %rbp
ret
네, 칼/ 아담의 분석을 통해 32비트에서 맞지 않는 칼/ 에 ,에비 promoted, to 32/ so,adam않 with that in a i, therefore- yes는네의 so맞지' agree int 따라서 다음과 같이 승격됩니다.long
★★★★★★★★★★★★★★★★★」long long
언급URL : https://stackoverflow.com/questions/18706859/why-is-this-an-undefined-behavior
'sourcecode' 카테고리의 다른 글
JavaScript를 사용한 예쁜 인쇄 JSON (0) | 2022.11.06 |
---|---|
결과 집합에 날짜 공백이 없는 날짜 범위의 날짜별 데이터 집계 (0) | 2022.11.06 |
json 경로(mariadb) (0) | 2022.11.06 |
div 안의 요소로 스크롤하는 방법 (0) | 2022.11.06 |
mysql 테이블의 컬럼 기본값을 변경하려면 어떻게 해야 합니까? (0) | 2022.11.06 |