스택큐힙리스트

64비트 미부호 정수를 실수로 변환하는 방법: g++로부터 이 알고리즘을 왜 사용하는 건가요? 본문

카테고리 없음

64비트 미부호 정수를 실수로 변환하는 방법: g++로부터 이 알고리즘을 왜 사용하는 건가요?

스택큐힙리스트 2023. 8. 29. 19:43
반응형

g++ 4.9.2를 사용하여 컴파일한다면

'bool int_dbl_com(const unsigned long long x, const double y)

{

return x <= y;

}

'

그러면 어셈블러의 출력(Windows x64 호출 규약에 대한)은 다음과 같습니다:

'testq %rcx, %rcx # x in RCX

js .L2

pxor %xmm0, %xmm0

cvtsi2sdq %rcx, %xmm0

ucomisd %xmm0, %xmm1 # y in XMM1

setae %al

ret

'

명령어 'cvtsi2sdq'는 부호 변환입니다. 첫 번째 테스트 및 점프 조합은 '%rcx < 0'를 확인하는 것입니다. 그렇다면, L2로 이동하고 이 부분은 이해하지 못합니다.

'.L2:

movq %rcx, %rax

andl $1, %ecx

pxor %xmm0, %xmm0

shrq %rax

orq %rcx, %rax

cvtsi2sdq %rax, %xmm0

addsd %xmm0, %xmm0

ucomisd %xmm0, %xmm1

setae %al

ret

'

이기적으로는, '%rcx' 를 절반으로 나누고, '%xmm0' 유형으로 변환한 다음 '%xmm0' 를 자신에게 추가하여 원래 값으로 돌아갈 수 있을 것입니다(물론, 64비트 정수에서 64비트 부동 소수점으로 변환할 때 일부 하위 순서 정확성이 손실된다는 것을 받아들입니다).

하지만 코드는 그렇게 동작하지 않습니다: '%rcx'의 가장 낮은 순서의 비트를 저장한 다음 결과에 이를 다시 or합니다. 왜일까요? 그리고 이러한 낮은 순서의 비트들은 어쨌든 손실될 것이므로 왜 귀찮게 저장하는 걸까요 (또는 나는 여기에서 잘못 생각하고 있는 걸까요)?

(최적화와 관계없이 동일한 알고리즘이 사용되는 것 같습니다. 여기서는 더 쉽게 확인하기 위해 -O3를 사용했습니다.)

답변 1

'.L2:

movq %rcx, %rax

andl $1, %ecx ; save the least significant bit of %rax

pxor %xmm0, %xmm0

shrq %rax ; make %rax represent half the original number, as a signed value

orq %rcx, %rax ; “round to odd”: if the division by two above was not exact, ensure the result is odd

cvtsi2sdq %rax, %xmm0 ; convert to floating-point

addsd %xmm0, %xmm0 ; multiply by two

ucomisd %xmm0, %xmm1 ; compare …

setae %al

ret

'

마지막 세 가지 명령은 소스 코드로부터 '<='와 'return'를 실행합니다. 다른 명령들은 'uint64_t'로부터 'double'로 변환하는 일부입니다.

이해하기 어려운 단계는 홀수로 반올림으로 주석을 달았다. 홀수로 반올림은 끔찍한 '“double rounding”' 의 부작용을 예방하는 기술이다.

사실 알고리즘은 64비트에서 63비트로 변환하고, 그 다음에 63비트에서 IEEE 754 이진64의 53비트 유효숫자로 변환하는 것입니다. 순진하게 구현할 경우, 경우에 따라서 이 두 가지 변환은 64비트 정수에서 53비트 유효숫자를 갖는 부동 소수점으로의 직접 단일 변환과 다른 결과를 출력할 수 있습니다. 이 현상을 double rounding(이중 반올림)이라고 합니다.

욕설입니다. 중간 반올림 결과가 두 번 반올림하는 경우에 잘못된 방향으로 반올림되지 않을 값을 가리킵니다. 이는 모든 입력에 대해 아래 시퀀스들을 동등하게 만드는 데 충분합니다.

'64-bit ---(round to odd)---> 63-bit ---(round to nearest even)----> binary64

64-bit -(round-to-nearest-even,the conversion the compiler wants)-> binary64

'

다른 측면에 대한 답변을 드리면:

하지만 코드가 하는 것은 그게 아닙니다. 이 코드는 '%rcx'의 최하위 비트를 저장하고, 그 결과에 이를 다시 논리합합니다. 왜죠? 그리고 이 최하위 비트들은 어차피 소실될 거 아닌가요? (아니면 제가 잘못 이해한 건가요?)

이 특정한 상황에서 round-to-odd를 구현하는 정확한 방법입니다. '%rcx'의 가장 미미한 비트는 시프트가 정확히 2로 나누어 떨어지지 않는 경우에만 1이며, 이 경우 결과값은 홀수로 만들어져야 합니다.

동일한 알고리즘은 최적화에 관계없이 사용되는 것 같습니다. 여기서는 가독성을 높이기 위해 -O3를 사용했습니다.

지시어 시퀀스는 최적화되어 있으며 (현대 프로세서 기준으로 볼 때), 'uint64_t' int에서 'double'으로 소스 수준의 변환과 일치합니다. 컴파일러는 최적화 수준에 관계없이 사용하기 위해 어떠한 노력도 필요하지 않습니다. 최적화시에는 (하지만 여기에서는 발생하지 않는다) 지시어가 다른 소스 수준 구조에 해당하는 다른 지시어와 융합될 수 있습니다. 그러나 '-O0'에 대한 변환의 최적 시퀀스와 다른 시퀀스를 갖는 것은 의미가 없습니다.

답변 2

64비트 부호 없는 정수를 double형으로 변환하는 알고리즘에 대해 설명하면서, 왜 g++에서 사용하는 알고리즘인지에 대해 서술하는 글을 작성해 드리겠습니다.

타이틀: g++에서 사용하는 부호 없는 64비트를 double형으로 변환하는 알고리즘의 이유

서론:

컴퓨터 프로그래밍은 다양한 언어와 컴파일러를 사용하여 다양한 작업을 수행합니다. 그 중에서 C++ 언어와 그의 컴파일러인 g++은 많은 프로그래머에게 널리 애용되는 도구입니다. 이번 글에서는 g++이 부호 없는 64비트 정수를 double형으로 변환하는 알고리즘을 채택한 이유에 대해 알아보겠습니다.

본론:

g++은 C++의 공식 컴파일러로, 많은 개발자들에게 널리 사용되고 있는 이유 중 하나는 코드 최적화에 뛰어나기 때문입니다. 64비트 부호 없는 정수를 double형으로 변환하는 알고리즘도 이러한 최적화에 기반하여 선택되었습니다.

컴퓨터에서 64비트 정수를 표현하는 방식은 보통 2의 보수 체계를 사용합니다. 이런 정수를 double형으로 변환하기 위해서는 절대값을 구한 후, 부호를 다시 적용하는 작업이 필요합니다. 구글의 컴파일러인 g++은 이런 변환 과정을 최적화함으로써 더 효율적인 코드를 생성할 수 있도록 도와줍니다.

g++은 정수를 실수로 변환할 때 캐스팅 연산이나 변환 함수를 사용하여 해결합니다. 부호 없는 64비트 정수의 경우, 이러한 변환을 직접 수행하는 것보다는 IEEE 754 표준에 따라 double형으로 변환하는 것이 성능 면에서 더 우수하기 때문에 이를 기본 알고리즘으로 선택한 것입니다.

IEEE 754는 부동소수점 연산을 위한 표준 규격으로, 많은 컴퓨터 시스템이 이를 따르고 있습니다. g++은 이 표준의 변환 규칙을 이용하여 부호 없는 64비트 정수를 double형으로 변환합니다. 이를 통해 변환 과정이 간결하고 성능이 향상되며, 정확한 결과를 보장할 수 있습니다.

마무리:

이렇듯 g++에서는 부호 없는 64비트 정수를 double형으로 변환하는 알고리즘을 최적화하여 사용합니다. 코드의 효율성을 높이고, IEEE 754 표준에 따라 정확한 결과를 보장하기 위해 이러한 선택을 하였습니다. g++을 사용하는 개발자들에게는 이 알고리즘이 고성능이면서 안정성이 뛰어난 솔루션을 제공하는 장점이 있습니다.

한편, g++을 사용할 때는 컴파일러의 최적화 플래그를 적절히 설정하여 원하는 결과를 얻을 수 있습니다. 또한, 부호 없는 64비트 정수를 double형으로 변환하는 과정에서 발생할 수 있는 잠재적인 문제에 대한 이해도를 가지고 사용하는 것이 중요합니다.

이상으로 g++에서 사용하는 부호 없는 64비트를 double형으로 변환하는 알고리즘에 대해 알아보았습니다. g++의 널리 사용되는 이유 중 하나인 코드 최적화와 안정성을 보여주는 실제 사례에 대해 알아봄으로써 C++ 개발자들에게 유익한 정보를 전달할 수 있었기를 바랍니다.

반응형
Comments