컴파일러는 왜 부동소수점 *2를 지수 증분으로 최적화하지 않는가?
을 실행 로 변환하는 것을 볼 수 .gcc는 실행 파일의 시프트로 변환합니다. 하다를 때도 일이 수 .int
a. a. a.float
를 들어.2 * f
순히entent entententent ententent ententent 를 증가시킬 수 f
1번, 1번, 1번, 1번, 1번, 1번.를 들어가 요구되었을 경우).-ffast-math
반적,, 그그??
으로 이할 정도로 컴파일러를 하여 수행해야 합니까?니면 이이 업업 접행 행? ???scalb*()
★★★★★★★★★★★★★★★★★」ldexp()/frexp()
★★★★★★★★★★★★★★★★?
예를 들어 2 * f는 단순히 f의 지수를 1씩 증가시켜 일부 사이클을 절약할 수 있습니다.
이건 전혀 사실이 아니야.
우선 0, 무한, 난, 데노말과 같은 코너 케이스가 너무 많습니다.그러면 성능 문제가 발생합니다.
지수를 늘리는 것이 곱셈을 하는 것보다 빠르지는 않다는 오해입니다.
하드웨어 지침을 살펴보면 지수를 증가시키는 직접적인 방법은 없습니다.그 대신 필요한 것은 다음과 같습니다.
- 비트 단위로 정수로 변환합니다.
- 지수를 늘립니다.
- 비트 단위로 부동소수로 변환합니다.
일반적으로 정수 및 부동소수점 실행 단위 간에 데이터를 이동하기 위한 중간에서 큰 지연 시간이 있습니다.따라서 결국 이 "최적화"는 단순한 부동 소수점 배수보다 훨씬 더 나빠집니다.
따라서 컴파일러가 이 "최적화"를 수행하지 않는 이유는 속도가 더 빠르지 않기 때문입니다.
최신 CPU에서 곱셈은 일반적으로 사이클당 1회의 throughput을 가지며 지연 시간이 짧습니다.값이 이미 부동소수점 레지스터에 있는 경우 표현에 정수 연산을 하기 위해 저글링하여 값을 이길 수 있는 방법은 없습니다.처음부터 메모리에 있고 현재 값이나 올바른 결과가 0, 데노멀, nan 또는 무한이 아니라고 가정할 경우 다음과 같은 작업을 수행하는 것이 더 빠를 수 있습니다.
addl $0x100000, 4(%eax) # x86 asm example
2를 곱하는 것, 즉 0과 무한대에서 경계가 있는 부동소수점 데이터의 전체 배열을 조작하고 있는 경우, 2의 거듭제곱을 하는 것만으로 데이터를 부동소수점 레지스터에 로드할 필요가 없습니다.
일반적인 부동소수점 형식(특히 IEEE 754)은 지수를 단순한 정수로 저장하지 않으므로 정수로 취급해도 올바른 결과를 얻을 수 없습니다.
32비트 플로트 또는 64비트 더블의 경우 지수 필드는 각각 8비트 또는 11비트입니다.지수 코드 1 ~ 254(플로트 단위) 또는 1 ~ 2046(이중 단위)은 정수처럼 작동합니다.이러한 값 중 하나에 1을 더하고 결과가 이러한 값 중 하나일 경우 표시되는 값은 2배가 됩니다.단, 다음과 같은 상황에서는 1개 추가가 실패합니다.
- 초기값은 0 이하입니다.이 경우 지수 필드는 0에서 시작하여 1을 더하면 2(플로트) 또는 2-1022(배)가 추가됩니다-126.이 값은 2배가 되지 않습니다.
- 초기값이 2(플로트) 또는1023 2(배)를127 초과합니다.이 경우 지수필드는 254 또는 2046에서 시작되며 1을 더하면 번호가 NaN으로 변경됩니다.이 값은 2배가 되지 않습니다.
- 초기값은 infinity 또는 NaN 입니다.이 경우 지수 필드는 255 또는 2047에서 시작하여 1을 추가하면 0으로 변경됩니다(그리고 부호 비트로 오버플로가 발생할 수 있습니다).결과는 0 또는 미정규이지만 각각 무한 또는 NaN이어야 합니다.
(위 내용은 긍정적인 징후에 대한 것입니다.상황은 마이너스 기호와 대칭입니다.)
다른 프로세서가 지적했듯이 일부 프로세서는 부동소수점 값의 비트를 신속하게 조작할 수 있는 기능을 갖추고 있지 않습니다.이 경우에도 지수 필드는 다른 비트에서 분리되지 않으므로 일반적으로 위의 마지막 경우 부호 비트에 오버플로하지 않고는 이 필드에 추가할 수 없습니다.
일부 응용 프로그램은 하위 정규성이나 NaN 또는 무한성을 무시하는 등의 바로 가기를 허용할 수 있지만 0을 무시할 수 있는 경우는 드뭅니다.지수에 1을 추가하면 0을 제대로 처리할 수 없기 때문에 사용할 수 없습니다.
컴파일러나 컴파일러 라이터가 똑똑하지 않은 것이 아닙니다.그것은 기준을 따르고 Infs, Nans, denormals와 같은 필요한 모든 "부작용"을 만들어내는 것에 가깝다.
또한 그것은 읽기 기억과 같이 요구되지 않는 다른 부작용을 일으키지 않는 것일 수 있다.하지만 어떤 상황에서는 더 빠를 수 있다는 건 인정해요.
실제로 하드웨어에서는 이렇게 됩니다.
2
는 또한 1.0의 가수와 2^1의 지수로 부동소수점 번호로 FPU에 전달됩니다.곱셈의 경우 지수를 가산하고 맨티사를 곱한다.
복잡한 케이스(2의 거듭제곱이 아닌 값의 곱)를 처리하는 전용 하드웨어가 있어 전용 하드웨어를 사용하는 경우와 마찬가지로 특별한 케이스도 취급하지 않기 때문에 회로와 명령을 추가하는 것은 의미가 없습니다.
GCC 10에서는 실제로 컴파일러의 최적화를 실시하고 있습니다.
x = 2.0 * hi * lo;
다음 코드를 생성합니다.
mulsd %xmm1, %xmm0 # x = hi * lo;
addsd %xmm0, %xmm0 # x += x;
임베디드 시스템 컴파일러에는 특별한 스케일 바이 2 의 의사 연산을 사용하는 것이 도움이 될 수 있습니다.이것은, 문제의 머신에 최적인 방법으로 코드 제너레이터에 의해서 번역될 수 있습니다.일부 임베디드 프로세서에서는, 2 배수의 풀 곱셈보다 훨씬 빠를 수 있기 때문입니다.곱셈이 가장 느린 임베디드 마이크로에서, 컴파일러는 아마도 런타임에 0인 가수 부분을 건너뛰기 위해 부동 소수점 변환 루틴이 인수를 체크하도록 함으로써 더 큰 성능 향상을 달성할 수 있을 것이다.
이전 Stackoverflow 질문에서는 2의 거듭제곱에 관한 질문입니다.합의와 실제 구현은 유감스럽게도 표준 곱셈보다 더 효율적인 방법은 없다는 것을 증명했습니다.
2를 곱하는 것이 지수를 1만큼 증가시킨다고 생각되면 다시 한 번 생각해 보십시오.IEEE 754 부동소수점 산술에서는 다음과 같이 생각할 수 있습니다.
케이스 1: Infinity와 NaN은 변경되지 않습니다.
사례 2: 가장 큰 지수를 가진 부동 소수점 숫자는 지수를 증가시키고 부호 비트를 제외한 가수 값을 0으로 설정하면 Infinity로 변경됩니다.
케이스 3: 가능한 최대 지수보다 작은 지수를 갖는 정규화된 부동 소수점 숫자는 그 지수를 1씩 증가시킵니다.이피!!!
케이스 4: 가장 높은 가수 비트세트를 가진 비 정규화 부동 소수점 수는 그 지수가 1 증가하여 정규화된 수치로 변환됩니다.
케이스 5: +0 및 -0을 포함한 가장 높은 가수 비트를 클리어한 탈규격 부동소수점 숫자는 그 가수 위치를 1비트 위치만큼 왼쪽으로 이동시켜 지수를 변경하지 않습니다.
이러한 모든 케이스를 올바르게 처리하는 정수 코드를 생성하는 컴파일러가 프로세서에 내장된 부동 소수점만큼 빠르지는 않을 것입니다.그리고 2.0의 곱셈에만 적합합니다.4.0 또는 0.5를 곱하는 경우 완전히 새로운 규칙 집합이 적용됩니다.또한 2.0 곱셈의 경우 x * 2.0을 x + x로 바꾸려고 할 수 있으며 많은 컴파일러가 이를 수행합니다.예를 들어 프로세서가 동시에 1개의 덧셈과 1개의 곱셈을 할 수 있지만 각 종류의 곱셈은 할 수 없기 때문입니다.따라서 다른 작업을 동시에 수행해야 하는 경우에 따라서는 x * 2.0을 선호할 수도 있고 x + x를 선호할 수도 있습니다.
언급URL : https://stackoverflow.com/questions/12919184/why-doesnt-a-compiler-optimize-floating-point-2-into-an-exponent-increment
'programing' 카테고리의 다른 글
URL 개체(이미지)에서 파일 개체를 만드는 방법 (0) | 2022.09.15 |
---|---|
최적의 GWT 위젯 라이브러리? (0) | 2022.09.15 |
왜 기억을 먹는 사람은 기억을 먹지 않는 걸까요? (0) | 2022.08.29 |
Android의 SQLite 특정 행을 업데이트하는 방법 (0) | 2022.08.29 |
size_t와 부호 없는 int의 차이점 (0) | 2022.08.28 |