부동소수점 계산에 문제가 있나요?
P粉071602406
P粉071602406 2023-08-20 16:48:26
0
2
595
<p>다음 코드를 고려해보세요: </p> <pre class="brush:js;toolbar:false;">0.1 + 0.2 == 0.3 -> </pre> <pre class="brush:js;toolbar:false;">0.1 + 0.2 -> 0.30000000000000004 </pre> <p>왜 이러한 부정확성이 발생합니까? </p>
P粉071602406
P粉071602406

모든 응답(2)
P粉347804896

하드웨어 디자이너의 관점

부동소수점 하드웨어를 설계하고 제작하기 때문에 하드웨어 디자이너의 관점을 추가해야 한다고 생각했습니다. 오류의 원인을 아는 것은 소프트웨어에서 무슨 일이 일어나고 있는지 이해하는 데 도움이 될 수 있으며, 궁극적으로 부동 소수점 오류가 발생하고 시간이 지남에 따라 누적되는 것처럼 보이는 이유를 설명할 수 있기를 바랍니다.

1. 개요

엔지니어링 관점에서 볼 때, 부동 소수점 계산을 수행하는 하드웨어는 마지막 비트에서 절반 미만의 오류 단위만 필요하기 때문에 대부분의 부동 소수점 연산에는 약간의 오류가 있습니다. 따라서 대부분의 하드웨어는 정밀도에서만 중지되며 마지막 비트의 절반 단위 미만의 오류만 생성하면 됩니다. 이는 부동 소수점 나누기에서 특히 문제가 됩니다. 단일 연산을 구성하는 것은 장치에서 허용하는 피연산자 수에 따라 다릅니다. 대부분의 장치에서는 피연산자가 2개이지만 일부 장치에서는 3개 이상의 피연산자를 허용합니다. 따라서 시간이 지남에 따라 오류가 누적되므로 반복 작업이 이상적인 오류를 생성한다는 보장은 없습니다.

2. 표준

대부분의 프로세서는 IEEE-754 표준을 따르지만 일부는 비정규화되거나 다른 표준을 사용합니다. 예를 들어, IEEE-754에는 정밀도를 희생하면서 매우 작은 부동 소수점 숫자를 표현할 수 있는 비정규화 모드가 있습니다. 그러나 다음 내용에서는 일반적인 동작 모드인 IEEE-754의 정규화 모드에 대해 설명합니다.

IEEE-754 표준에서 하드웨어 설계자는 마지막 비트의 절반 단위 미만인 한 모든 오류/ε 값을 허용할 수 있으며, 결과는 한 번의 작업에 대해서만 마지막 비트의 절반 단위 미만이어야 합니다. . 이는 작업이 반복될 때 오류가 누적되는 이유를 설명합니다. IEEE-754 배정밀도의 경우 가수(예: 5.3e5의 5.3)라고도 하는 부동 소수점 숫자의 숫자 부분(정규화)을 나타내는 데 53비트가 사용되므로 이는 비트 54입니다. 다음 몇 섹션에서는 다양한 부동 소수점 연산에서 하드웨어 오류의 원인을 자세히 설명합니다.

3. 나누기 반올림 오류의 원인

부동소수점 나눗셈에서 오류가 발생하는 주요 원인은 몫을 계산하는 데 사용되는 나눗셈 알고리즘입니다. 대부분의 컴퓨터 시스템은 주로 Z=X/YZ = X * (1/Y)中。除法是迭代计算的,即每个周期计算一些商的位数,直到达到所需的精度,对于IEEE-754来说,这是任何误差小于最后一位的一个单位的情况。Y的倒数表(1/Y)被称为慢除法中的商选择表(QST),商选择表的位数通常是基数的宽度,或者在每次迭代中计算的商的位数加上几个保护位。对于IEEE-754标准的双精度(64位),它将是除法器的基数大小加上几个保护位k,其中k>=2。因此,例如,一个典型的每次计算2位商(基数为4)的除法器的商选择表将是2+2= 4 비트(몇 가지 선택적 비트 포함)에서 나눗셈 계산에 곱셈의 역원을 사용합니다.

3.1 나눗셈 반올림 오류: 역수의 근사

몫 선택 테이블의 역수는 나눗셈 방법에 따라 다릅니다. 느린 나눗셈(예: SRT 나눗셈) 또는 빠른 나눗셈(예: Goldschmidt 나눗셈) 각 항목은 가능한 가장 낮은 오류를 생성하기 위해 나눗셈 알고리즘에 따라 수정됩니다. 그럼에도 불구하고 모든 역수는 실제 역수의 근사치이며 어느 정도 오류가 발생합니다. 느린 나눗셈 방법과 빠른 나눗셈 방법 모두 반복적으로 몫을 계산합니다. 즉, 각 단계에서 특정 개수의 몫 자릿수가 계산된 다음 그 결과를 피제수에서 빼고 제수는 오류가 단위의 절반 미만이 될 때까지 이 단계를 반복합니다. 마지막 숫자. 느린 나눗셈 방법은 각 단계에서 고정된 수의 몫 자릿수를 계산하고 일반적으로 비용이 저렴한 반면, 빠른 나눗셈 방법은 각 단계에서 가변 수의 몫 자릿수를 계산하며 일반적으로 더 비쌉니다. 나눗셈 방법의 가장 중요한 부분은 대부분이 역수에 대한 근사치의 반복적인 곱셈에 의존하므로 오류가 발생하기 쉽다는 것입니다.

4. 다른 작업의 반올림 오류: 잘림

모든 작업에서 반올림 오류가 발생하는 또 다른 원인은 IEEE-754에서 허용하는 다양한 잘림 모드입니다. 잘림, 0에 가까운 반올림, 반올림(기본값) , 반올림, 반올림이 있습니다. 모든 방법은 단일 작업에 대해 마지막 비트 단위보다 작은 오류를 발생시킵니다. 시간이 지남에 따라 작업이 반복되면서 잘림이 누적되어 결과 오류가 발생합니다. 이러한 잘림 오류는 특정 형태의 반복 곱셈이 포함되는 지수 산술에서 특히 문제가 됩니다.

5. 작업을 반복하세요

부동 소수점 계산을 수행하는 하드웨어는 단일 작업에서 마지막 비트의 오류 단위의 절반 미만인 결과만 생성하면 되므로 모니터링하지 않으면 작업이 반복됨에 따라 오류가 커집니다. 이것이 제한된 오류가 필요한 계산에서 수학자들이 IEEE-754에서 반올림 마지막 짝수를 사용하는 것과 같은 방법을 사용하는 이유입니다. 오류는 시간이 지남에 따라 서로 상쇄될 가능성이 더 높기 때문입니다. 간격 산술IEEE 754 반올림 모드를 사용하여 반올림 오류를 예측하고 수정합니다. 가장 가까운 짝수(마지막 숫자)로 반올림하는 것은 다른 반올림 모드에 비해 상대 오차가 낮기 때문에 IEEE-754의 기본 반올림 모드입니다.

가장 가까운 짝수로 반올림하는 기본 반올림 모드는 마지막 자릿수의 1/2 단위 미만의 오류로 연산을 보장한다는 점에 유의하세요. 자르기, 반올림, 반내림만 사용하세요.

P粉790187507

바이너리부동 소수점이것이 수학이 작동하는 방식입니다. 대부분의 프로그래밍 언어에서는 IEEE 754 표준을 기반으로 합니다. 문제의 핵심은 숫자가 이 형식에서 정수 곱하기 2의 거듭제곱으로 표현된다는 것입니다. 분모가 2의 거듭제곱이 아닌 유리수(예: 0.1,即1/10)는 정확하게 표현될 수 없습니다.

표준 binary64格式中的0.1의 경우 표현은

로 정확하게 작성할 수 있습니다.
  • 십진수:0.1000000000000000055511151231257827021181583404541015625
  • C99 hexfloat 표기법: 0x1.999999999999ap-4

반대로 유리수 0.1,即1/10

와 똑같이 쓸 수 있습니다.
  • 십진수:0.1
  • C99 hexfloat 표기법과 유사한 표현: 0x1.99999999999999...p-4,其中...9의 끝없는 시퀀스를 나타냅니다.

프로그램의 상수 0.20.3도 실제 값과 유사합니다. 우연히 0.2에 가장 가까운 double이 유리수 0.2보다 크지만 가장 가까운 double 0.3 code>는 유리수 0.3보다 작습니다. 0.20.3也将近似于它们的真实值。恰好,最接近0.2double大于有理数0.2,但最接近0.3double小于有理数0.30.10.2的和最终比有理数0.30.2의 합은 결국 유리수 0.3보다 커지므로 코드의 상수와 일치하지 않습니다.

부동 소수점 산술 문제에 대한 상당히 포괄적인 처리 방법은 모든 컴퓨터 과학자가 부동 소수점 산술에 대해 알아야 할 사항입니다. 더 이해하기 쉬운 설명은 floating-point-gui.de를 참조하세요.

일반 십진수(10진수)에도 동일한 문제가 존재합니다. 이것이 바로 1/3과 같은 숫자가 결국 0.333333333이 되는 이유입니다...

십진법으로는 쉽게 표현할 수 있지만 이진법으로는 표현할 수 없는 숫자(3/10)를 발견하셨습니다. 그 반대도 어느 정도 사실입니다. 1/16은 십진수로 보면 보기 흉한 숫자(0.0625)이지만, 이진수로 보면 십진수로 1만 분의 1(0.0001)만큼 깔끔해 보입니다.* * - 사용하는 데 익숙했다면 일상생활에서 사용되는 이진수 체계를 보면, 그 숫자를 보면서 계속 2로 나누어서 얻을 수 있다는 것을 본능적으로 이해할 것입니다.

물론 이것은 부동 소수점 숫자가 메모리에 저장되는 방식이 아닙니다(과학 표기법 형식을 사용함). 그러나 이는 우리가 일반적으로 관심을 갖는 "실제" 숫자가 10의 거듭제곱인 경향이 있기 때문에 이진 부동 소수점 정밀도 오류가 발생하는 경향이 있다는 문제를 보여줍니다. 그러나 이는 우리가 매일 십진수 시스템을 사용하기 때문입니다. -일 기준. 이것이 바로 "7/7" 대신에 71%라고 말하는 이유입니다(71%는 대략적인 값입니다. 5/7은 어떤 십진수로도 정확하게 표현할 수 없기 때문입니다).

아니요, 이진 부동 소수점 숫자에는 아무런 문제가 없습니다. 다른 기본 N 숫자 시스템처럼 불완전할 뿐입니다 :)

실제로 이 정밀도 문제는 부동 소수점 숫자를 표시하기 전에 관심 있는 소수 자릿수로 반올림하기 위해 반올림 기능을 사용해야 함을 의미합니다.

또한 등호 테스트를 특정 허용오차를 허용하는 비교로 대체해야 합니다. 즉,

사용하지 마세요if (x == y) { ... }

대신 if (abs(x - y) < myToleranceValue) { ... }을 사용하세요.

특정 애플리케이션에 따라 abs是绝对值函数。myToleranceValue 선택해야 하는 것은 허용할 준비가 된 "흔들기 공간"의 양과 비교할 최대 수(정확도 손실 문제로 인해)와 많은 관련이 있습니다. 선택한 언어의 "엡실론" 스타일 상수에 유의하세요. 이러한 상수는 공차 값으로 사용될 수 있지만 숫자가 큰 계산은 엡실론 임계값을 초과할 수 있으므로 효율성은 다루는 숫자의 크기에 따라 달라집니다.

최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿