Win32에서 서명되지 않은 int에 대한 더블캐스트가 2,147,483,648로 잘립니다.
다음 코드 컴파일:
double getDouble()
{
double value = 2147483649.0;
return value;
}
int main()
{
printf("INT_MAX: %u\n", INT_MAX);
printf("UINT_MAX: %u\n", UINT_MAX);
printf("Double value: %f\n", getDouble());
printf("Direct cast value: %u\n", (unsigned int) getDouble());
double d = getDouble();
printf("Indirect cast value: %u\n", (unsigned int) d);
return 0;
}
출력(MSVC x86) :
INT_MAX: 2147483647
UINT_MAX: 4294967295
Double value: 2147483649.000000
Direct cast value: 2147483648
Indirect cast value: 2147483649
출력(MSVC x 64):
INT_MAX: 2147483647
UINT_MAX: 4294967295
Double value: 2147483649.000000
Direct cast value: 2147483649
Indirect cast value: 2147483649
Microsoft 문서에서는 변환 시 부호 있는 정수 최대값에 대한 언급은 없습니다.double
로로 합니다.unsigned int
.
의 값INT_MAX
to are are 、 are 、 려 、 are 、 are 、 are 。2147483648
함수의 반환일 때.
Visual Studio 2019를 사용해서 프로그램을 만들고 있습니다.이것은 gcc에서는 발생하지 않습니다.
가가뭘 ?못 ???요?double
로로 합니다.unsigned int
컴파일러 버그...
는 @anastaciu를 합니다.__ftol2_sse
이것은 숫자를 부호 있는 길이로 변환하는 것 같습니다.루틴명은ftol2_sse
이는 SSE 지원 기계이지만 플로트가 x87 부동 소수점 레지스터에 있기 때문입니다.
; Line 17
call _getDouble
call __ftol2_sse
push eax
push OFFSET ??_C@_0BH@GDLBDFEH@Direct?5cast?5value?3?5?$CFu?6@
call _printf
add esp, 8
반면 간접 캐스팅은 그렇다.
; Line 18
call _getDouble
fstp QWORD PTR _d$[ebp]
; Line 19
movsd xmm0, QWORD PTR _d$[ebp]
call __dtoui3
push eax
push OFFSET ??_C@_0BJ@HCKMOBHF@Indirect?5cast?5value?3?5?$CFu?6@
call _printf
add esp, 8
변수에 후하고 SSE를 호출합니다.__dtoui3
두 번에서 서명 없이 변환하는 루틴입니다
직접 주조물의 거동은 C89에 준거하지 않으며 이후 개정판에도 준거하지 않는다. 심지어 C89도 다음과 같이 명시하고 있다.
정수형 값을 부호 없는 유형으로 변환한 나머지 작업은 부동형 값을 부호 없는 유형으로 변환한 경우 수행할 필요가 없습니다.따라서 휴대용 값의 범위는 [0, Utype_MAX + 1)입니다.
2005년 이후 계속된 문제일 수 있다고 생각합니다.이전에는 변환 기능이 있었습니다.__ftol2
이 코드에서는 아마 동작했을 것입니다.즉, 값을 부호 있는 번호 -2147483647로 변환하여 부호 없는 번호를 해석했을 때 올바른 결과를 얻을 수 있습니다.
도 ★★★★★★★★★★★★★★★.__ftol2_sse
is 、 。__ftol2
의 비트를 취하는 , 「」를 반환하는 의 에러를 있습니다.LONG_MIN
0x80000000
여기서 부호 없이 길게 해석되는 것은 기대했던 것과 전혀 다릅니다.★★★★★★★★★★★★★★★의 동작__ftol2_sse
signed long
value > double a value >의 으로서 「double a value」를 선택합니다LONG_MAX
로로 합니다.signed long
정의되지 않은 동작을 했을 것입니다.
@AntiHaapala의 답변에 따라 최적화를 사용하여 코드를 테스트했습니다./Ox
이것으로 버그가 제거되는 것을 알 수 있었습니다.__ftol2_sse
는 사용되지 않게 되었습니다.
//; 17 : printf("Direct cast value: %u\n", (unsigned int)getDouble());
push -2147483647 //; 80000001H
push OFFSET $SG10116
call _printf
//; 18 : double d = getDouble();
//; 19 : printf("Indirect cast value: %u\n", (unsigned int)d);
push -2147483647 //; 80000001H
push OFFSET $SG10117
call _printf
add esp, 28 //; 0000001cH
최적화 기능(인라인)getdouble()
고정 표현식 평가를 추가함으로써 런타임에 변환할 필요가 없어지고 버그가 사라집니다.
궁금해서 몇 가지 테스트를 더 했습니다. 즉, 런타임에 float-to-int 변환을 강제하도록 코드를 변경했습니다.이 경우 결과는 여전히 정확합니다.컴파일러는 최적화 기능을 사용하여__dtoui3
두 변환 모두:
//; 19 : printf("Direct cast value: %u\n", (unsigned int)getDouble(d));
movsd xmm0, QWORD PTR _d$[esp+24]
add esp, 12 //; 0000000cH
call __dtoui3
push eax
push OFFSET $SG9261
call _printf
//; 20 : double db = getDouble(d);
//; 21 : printf("Indirect cast value: %u\n", (unsigned int)db);
movsd xmm0, QWORD PTR _d$[esp+20]
add esp, 8
call __dtoui3
push eax
push OFFSET $SG9262
call _printf
단, 인라이닝을 방지하기 위해__declspec(noinline) double getDouble(){...}
이 버그를 되돌립니다.
//; 17 : printf("Direct cast value: %u\n", (unsigned int)getDouble(d));
movsd xmm0, QWORD PTR _d$[esp+76]
add esp, 4
movsd QWORD PTR [esp], xmm0
call _getDouble
call __ftol2_sse
push eax
push OFFSET $SG9261
call _printf
//; 18 : double db = getDouble(d);
movsd xmm0, QWORD PTR _d$[esp+80]
add esp, 8
movsd QWORD PTR [esp], xmm0
call _getDouble
//; 19 : printf("Indirect cast value: %u\n", (unsigned int)db);
call __ftol2_sse
push eax
push OFFSET $SG9262
call _printf
__ftol2_sse
양쪽 변환에서 호출되어 출력이 생성됩니다.2147483648
두 경우 모두 @zwol 의혹은 옳았다.
컴파일 상세:
- 명령줄 사용:
cl /permissive- /GS /analyze- /W3 /Gm- /Ox /sdl /D "WIN32" program.c
Visual Studio에서:
무효화
RTC
에->
Properties->
Code Generation 기본 런타임 체크를 기본값으로 설정합니다.에서의 최적화 활성화
->
Properties->
Optimization Optimization을 /Ox로 설정합니다.★★★★★★★의 디버거 사용
x86
.__ftol2_sse
.
에서 부호 있는 「x87」로할 수 .int
long
), (Windows의 경우 32비트타입)에 하게 접속하지 ,uint32_t
.
정수 결과를 오버플로 하는 x86 FP -> 정수 명령어는 단순히 랩/삭제하는 것이 아닙니다.이 명령어는 수신처에 정확한 값이 표시되지 않을 때 인텔에서 "integer infinite"라고 부르는 것을 생성합니다.즉, 고비트 세트, 기타 비트 클리어입니다.
(또는 FP 무효 예외가 마스크되지 않은 경우 FP가 기동하여 값이 저장되지 않습니다.단, 기본 FP 환경에서는 모든 FP 예외가 마스크됩니다.따라서 FP 계산에서는 장애 대신 NaN을 얻을 수 있습니다.)
여기에는 (현재 반올림 모드 사용)와 같은 x87 명령과 (0을 향해 잘라내기 사용)와 같은 SSE2 명령이 모두 포함됩니다.t
수단)을 참조해 주세요.
편집하는 입니다.double
->unsigned
의 __ftol2_sse
.
사이드 노트/탄젠트:
FP -> 를 x86-64 의 FP -> uint32_t 로 할 수 .cvttsd2si rax, xmm0
64비트 서명 수신처로 변환하여 정수 수신처의 Low Half(EAX; 로우 하프)에서 원하는 uint32_t를 생성합니다.
결과가 0을 벗어나면 C와 C++ UB가 됩니다.2^32-1 범위이므로 큰 양의 값 또는 음의 값이 RAX(EAX)의 하위 절반을 정수 비트 패턴에서 남길 수 있습니다(정수 변환과 달리 값의 모듈로 축소는 보장되지 않습니다).부호 없는 음의 이중 int를 주조하는 동작이 C 표준에 정의되어 있는가? ARM과 x86의 동작은 다릅니다.명확하게 말하면, 질문에 정의되어 있지 않거나, 실장 정의의 동작도 포함되어 있지 않습니다.즉, FP->int64_t가 있으면 FP->uint32_t를 효율적으로 구현할 수 있다는 것입니다.여기에는 x87이 포함됩니다.fistp
는, 64비트 모드에서 64비트 정수만을 직접 처리할 수 있는SSE2 명령과는 달리, 32비트 및 16비트 모드에서도 64비트 정수 수신처를 쓸 수 있습니다.
언급URL : https://stackoverflow.com/questions/63983079/double-cast-to-unsigned-int-on-win32-is-truncating-to-2-147-483-648
'programing' 카테고리의 다른 글
SDL과 OpenGL이 관련된 이유 (0) | 2022.08.14 |
---|---|
vue-module, nginx 및 직접 링크 (0) | 2022.08.14 |
Vuejs 각 루프에 계산된 속성을 사용하는 방법 (0) | 2022.08.13 |
반환값이 무시될 경우 경고는 어떻게 발생합니까? (0) | 2022.08.13 |
바이트 버퍼를 서명해야 합니까, 아니면 서명되지 않은 문자 버퍼로 해야 합니까? (0) | 2022.08.13 |