math - 아두 이노 퍼센트




부동 소수점 수학이 깨졌습니까? (18)

다음 코드를 살펴보십시오.

0.1 + 0.2 == 0.3  ->  false
0.1 + 0.2         ->  0.30000000000000004

왜 이러한 부정확성이 발생합니까?


하드웨어 디자이너의 관점

부동 소수점 하드웨어를 설계하고 제작 한 이후로 하드웨어 디자이너의 관점을 추가해야한다고 생각합니다. 오류의 원인을 알면 소프트웨어에서 어떤 일이 일어나는지 이해하는 데 도움이 될 수 있으며 궁극적으로는 부동 소수점 오류가 발생하고 시간이 지남에 따라 누적되는 이유에 대한 설명이 도움이되기를 바랍니다.

1. 개요

엔지니어링 관점에서, 대부분의 부동 소수점 연산은 부동 소수점 계산을 수행하는 하드웨어가 마지막 위치에서 한 단위의 절반 이하의 오차를 가지기 때문에 오류의 일부 요소를 갖습니다. 따라서 대부분의 하드웨어는 부동 소수점 분할에서 특히 문제가되는 단일 연산 의 마지막 위치에서 한 단위의 절반 이하의 오차를 산출하는 데 필요한 정밀도로 정지합니다. 하나의 연산을 구성하는 것은 단위가 갖는 피연산자 수에 달려 있습니다. 대부분의 경우 두 개이지만 일부 유닛은 3 개 이상의 피연산자를 사용합니다. 이 때문에 오류가 시간이 지남에 따라 합산되기 때문에 반복되는 작업으로 바람직한 오류가 발생한다는 보장이 없습니다.

2. 표준

대부분의 프로세서는 IEEE-754 표준을 따르지만 일부는 비정규 화 된 표준이나 다른 표준을 사용합니다. 예를 들어 IEEE-754에는 매우 작은 부동 소수점 수를 표현할 수있는 비표준 모드가 있습니다. 그러나 다음은 일반적인 작동 모드 인 IEEE-754의 표준화 모드를 다룹니다.

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

3. 사업부의 반올림 오류의 원인

부동 소수점 나누기 오류의 주요 원인은 몫을 계산하는 데 사용되는 나누기 알고리즘입니다. 대부분의 컴퓨터 시스템은 주로 Z=X/Y , Z = X * (1/Y) 의 역행렬 곱셈을 사용하여 나눗셈을 계산합니다. 나눗셈은 반복적으로 계산됩니다. 즉, 원하는 정밀도에 도달 할 때까지 각 사이클에서 몫의 일부 비트가 계산됩니다. IEEE-754의 경우 마지막 부분에서 오류가 한 개 미만인 항목입니다. Y (1 / Y)의 역수 표는 저속 분할의 몫 선택 표 (QST)로 알려져 있으며 몫 선택 표의 비트 수는 일반적으로 기수의 폭 또는 각 반복에서 계산 된 몫과 몇 개의 가드 비트. IEEE-754 표준의 경우 배정도 (64 비트)의 경우 기수 기수의 크기와 몇 개의 보호 비트 k ( k>=2 됩니다. 예를 들어, 한 번에 2 비트의 지수 (기수 4)를 계산하는 분배기에 대한 일반적인 지수 선택 표는 2+2= 4 비트 (몇 개의 선택적 비트를 더한 것)가됩니다.

3.1 분할 라운딩 오류 : 상호 근사

몫 선택 테이블에있는 역수는 분할 방법 에 따라 달라집니다 : SRT 분할과 같은 느린 분할 또는 Goldschmidt 분할과 같은 빠른 분할; 각각의 엔트리는 가장 낮은 가능한 에러를 산출하기 위해 분할 알고리즘에 따라 수정된다. 그러나 어떤 경우에도 모든 역수는 실제 역의 근사치 이며 오류의 일부 요소를 도입합니다. 느린 나눗셈과 빠른 나눗셈 방법 모두 몫을 반복적으로 계산합니다. 즉, 몫의 일부 비트가 각 단계에서 계산 된 다음 결과가 배당에서 뺍니다. 그리고 나누기는 오류가 1/2보다 작아 질 때까지 단계를 반복합니다 마지막 장소에있는 유닛. 저속 분할 방법은 각 단계에서 고정 소수점 자릿수를 계산하고 일반적으로 구축 비용이 저렴하며 빠른 분할 방법은 단계 당 가변 숫자 자릿수를 계산하며 일반적으로 구축하는 데 더 많은 비용이 듭니다. 분할 방법의 가장 중요한 부분은 대부분이 상호 근사 로 반복 곱하기에 의존하기 때문에 오류가 발생하기 쉽습니다.

4. 다른 연산의 반올림 오류 : 잘림

모든 연산의 반올림 오류의 또 다른 원인은 IEEE-754가 허용하는 최종 답을 잘라내는 다른 모드입니다. 잘림, 0으로 반올림 , 가장 가까운 수로 반올림 (기본값), 반올림 및 반올림이 있습니다. 모든 방법은 단일 작업의 마지막 위치에 하나의 단위 미만의 오차 요소를 도입합니다. 시간이지나면서 반복되는 작업에서 잘라내 기는 결과 오류에 누적 적으로 추가됩니다. 이 절단 오류는 지수 계산에서 특히 문제가되는데 여기에는 반복되는 곱셈이 포함됩니다.

5. 반복 된 작업

부동 소수점 계산을 수행하는 하드웨어는 단일 작업의 마지막 위치에서 한 단위의 절반 이하의 오류로 결과를 산출해야하기 때문에 관찰되지 않으면 반복되는 작업에서 오류가 커집니다. 이것은 제한된 오류가 필요한 계산에서 수학자가 IEEE-754 의 마지막 자리에서 가장 가까운 를 반올림하는 것과 같은 방법을 사용하는 이유입니다. 왜냐하면 시간이 지남에 따라 오류가 서로 상쇄되기 쉽기 때문입니다 out 및 Interval 산술 연산IEEE 754 반올림 모드 의 변형을 결합하여 반올림 오류를 예측하고 수정합니다. 다른 반올림 모드와 비교할 때 상대적 오류가 적기 때문에 가장 가까운 자리로 반올림 (마지막 위치에서 반올림)하는 것은 IEEE-754의 기본 반올림 모드입니다.

마지막 반올림 모드 인 마지막 반올림 모드는 한 번의 작업에 대해 마지막 위치에서 한 단위의 절반 이하의 오류를 보장합니다. 잘라 내기, 올림 및 반올림을 사용하면 마지막 위치에서 한 단위의 절반보다 크지 만 마지막 위치에서 한 단위보다 작은 오류가 발생할 수 있으므로 이러한 모드는 권장되지 않습니다 간격 산술에 사용됩니다.

6. 요약

즉, 부동 소수점 연산의 오류에 대한 근본적인 이유는 하드웨어의 잘라내기를 사용하는 경우와 나눗셈의 경우에 역수를 잘라 버리는 것입니다. IEEE-754 표준은 단일 작업의 마지막 위치에서 한 단위의 절반 이하의 오류 만 필요하기 때문에 반복 작업에 대한 부동 소수점 오류는 수정하지 않으면 합산됩니다.


아니, 깨진 것은 아니지만 대부분의 소수점을 근사화해야합니다.

개요

부동 소수점 산수 정확합니다. 불행히도 보통 10 진수 표현과 잘 맞지 않으므로 우리가 쓴 것에서 약간 벗어난 입력을 얻는 경우가 많습니다.

0.01, 0.02, 0.03, 0.04 ... 0.24와 같은 간단한 숫자조차도 이진 소수로 정확하게 표현할 수 없습니다. 0.01, .02, .03 ...을 계산하면 0.25가 될 때까지는 기본 2 에서 표현할 수있는 첫 번째 분수를 얻지 못합니다 . FP를 사용해 보았을 때 0.01이 약간 벗어 났으므로 0.25를 25로 정확하게 추가하는 유일한 방법은 보호 비트와 반올림과 관련된 긴 인과 관계 체인을 필요로했을 것입니다. 예측하기가 힘들어서 손을 내밀고 "FP가 정확하지 않습니다 "라고 말하지만 사실이 아닙니다.

우리는 계속해서 FP 하드웨어에베이스 10에서 단순 해 보이지만베이스 2에서 반복되는 부분을 제공합니다.

어떻게 이런일이 일어 났습니까?

우리가 10 진수로 쓸 때, 모든 분수 (특히, 모든 종결 십진수) 는 형식의 합리적인 수입니다

a / ( 2n × 5m )

바이너리에서는 2 n 항만 얻습니다 . 즉 :

a / 2n

진수 그래서, 우리는 나타낼 수 없습니다 (1) / 3 . 기본 10은 2를 주요 인자로 포함하기 때문에 이진 소수 로 쓸 수있는 모든 숫자 기본 10 분수로 쓸 수 있습니다. 그러나 기본 10 분수 로 쓰는 것은 거의 2 진수로 표현할 수 있습니다. 범위에서 0.01, 0.02, 0.03 ... 0.99은 단지 세 개의 숫자는 우리 FP 형식으로 표현 될 수있다 : 0.25, 0.50, 및 0.75들은 1/4, 1/2, 3/4, 모든 숫자 때문에 2 n 항만을 사용하는 소수 요소가 있습니다.

기본으로 10 우리는 나타낼 수 없습니다 (1) / 3 . 그러나 바이너리에서는 1 / 10 또는 1 / 3을 할 수 없습니다 .

따라서 모든 이진수는 십진수로 쓰여질 수 있지만 그 반대는 사실이 아닙니다. 사실 대부분의 십진수는 2 진수로 반복됩니다.

그것을 다루기.

개발자는 일반적으로 <엡실론 비교를 지시 받으며, 더 나은 조언은 C 라이브러리 (round () 및 roundf ())에서 정수 값으로 반올림하여 FP 형식으로 유지하는 것일 수 있습니다. 특정 소수 자릿수로 반올림하면 출력과 관련된 대부분의 문제가 해결됩니다.

또한 실수 계산식 문제 (FP가 초기에 고의로 값 비싼 컴퓨터에서 고안된 문제)에서 우주의 물리적 상수와 다른 모든 측정치는 상대적으로 적은 수의 유효 숫자로만 알려져 있기 때문에 전체 문제 공간 어쨌든 "부정확"했습니다. FP "정확도"는 이런 종류의 응용에서 문제가되지 않습니다.

전체 문제는 사람들이 콩 계산에 FP를 사용하려고 할 때 실제로 발생합니다. 그것은 그것을 위해 작동하지만, 당신이 정수 값에 충실 할 때만, 어떤 종류의 그것을 사용하는 지점을 패배시킵니다. 이것이 우리가 모든 소수 소프트웨어 라이브러리를 가지고있는 이유입니다.

가 Pizza 답변을 좋아합니다 . 실제 문제를 설명하기 때문이며, 평범하지 않은 "부정확"에 대해서만 설명하기 때문입니다. FP가 단순히 "부정확하다"면, 우리는 그것을 고칠 수 있었고 수십 년 전에 그것을했을 것입니다. 우리가하지 못한 이유는 FP 형식이 작고 빠르며 많은 수를 처리하는 가장 좋은 방법이기 때문입니다. 또한 소규모 메모리 시스템을 사용하는 매우 느린 컴퓨터로 큰 문제를 해결하기위한 우주 시대와 무기 경쟁 및 초기 시도의 유산이기도합니다. (때로는 1 비트 저장을위한 개별 자기 코어도 있지만 다른 이야기입니다. )

결론

은행에서 빈을 계산하는 경우, 처음에는 십진수 표현을 사용하는 소프트웨어 솔루션이 완벽하게 잘 작동합니다. 하지만 양자 역학이나 공기 역학은 그렇게 할 수 없습니다.


다른 정답뿐 아니라 부동 소수점 산술로 인한 문제를 피하기 위해 값을 스케일링하는 것이 좋습니다.

예 :

var result = 1.0 + 2.0;     // result === 3.0 returns true

... 대신에:

var result = 0.1 + 0.2;     // result === 0.3 returns false

0.1 + 0.2 === 0.3 표현식은 JavaScript에서 false 를 반환하지만 다행히도 부동 소수점의 정수 연산은 정확하므로 스케일링을 통해 10 진 표현 오류를 피할 수 있습니다.

실용적인 예로서, 정확도가 가장 중요한 부동 소수점 문제를 피하려면, 센트 수를 나타내는 정수 ( 25.50 달러 대신 2550 센트)로 돈을 처리하는 것이 좋습니다.

1 Douglas Crockford : JavaScript : 좋은 부분 : 부록 A - 어려운 부분들 (105 페이지) .


부동 소수점 반올림 오류. 0.1은 기본 소수점 5의 누락으로 인해 기수 10에서와 같이 기수 2에서 정확하게 표현할 수 없습니다. 1/3이 10 진수로 표현할 때 무한한 자릿수를 사용하지만 3 진수에서는 "0.1"인 것처럼, 0.1은 10 진수가 아닌 2 진수의 무한 수를 취합니다. 그리고 컴퓨터에는 무한한 양의 메모리가 없습니다.


컴퓨터에 저장된 부동 소수점 숫자는 기본 부분과 정수 부분이 곱해진 정수와 지수의 두 부분으로 구성됩니다.

컴퓨터가 기본 10에서 작동하는 경우 0.11 x 10⁻¹ , 0.22 x 10⁻¹ , 0.33 x 10⁻¹ 됩니다. 정수 수학은 쉽고 정확하므로 0.1 + 0.2 를 추가하면 분명히 0.3 됩니다.

컴퓨터는 일반적으로 기본 10에서 작동하지 않고 기본 2에서 작동합니다. 일부 값에 대해서는 정확한 결과를 얻을 수 있습니다. 예를 들어 0.51 x 2⁻¹ 이고 0.251 x 2⁻² . 결과를 추가하면 3 x 2⁻² 또는 0.75 이다. 정확하게.

문제는 기본 10에서 정확히 나타낼 수 있지만 기본 2에서 표시 할 수없는 숫자와 함께 제공됩니다.이 수는 가장 가까운 등가 수로 반올림해야합니다. 가장 일반적인 IEEE 64 비트 부동 소수점 형식을 가정 할 때, 0.1 가장 가까운 숫자는 3602879701896397 x 2⁻⁵⁵ 이고, 가장 가까운 숫자는 7205759403792794 x 2⁻⁵⁵ . 7205759403792794 x 2⁻⁵⁵ ; 이들을 함께 10808639105689191 x 2⁻⁵⁵ 또는 정확한 십진수 값 0.3000000000000000444089209850062616169452667236328125 됩니다. 부동 소수점 숫자는 일반적으로 표시를 위해 반올림됩니다.


내 대답은 꽤 길기 때문에 세 부분으로 나눠 봤습니다. 문제는 부동 소수점 수학에 관한 것이므로 기계가 실제로하는 것에 중점을 둡니다. 필자는 또한 두 배 (64 비트)의 정밀도를 위해이 함수를 만들었지 만 인수는 모든 부동 소수점 산술에 동일하게 적용됩니다.

전문

IEEE 754 배정 밀도 이진 부동 소수점 형식 (binary64) 숫자는 숫자의 형식을 나타냅니다.

값 = (-1) ^ s * (1.m 51m 50 ... m 2m 1 m 0 ) 2 * 2 e-1023

64 비트 :

  • 첫 번째 비트는 부호 비트입니다 . 숫자가 음수 0 1 , 그렇지 않으면 0 입니다.
  • 다음 11 비트는 exponent 이며 1023 offset 됩니다. 즉, 배정도 숫자에서 지수 비트를 읽은 후 1023을 빼서 2의 거듭 제곱을 구해야합니다.
  • 나머지 52 비트는 significand (또는 가수)입니다. 가수의 경우, 이진 값의 최상위 비트가 1 이기 때문에 'implied'1 1. 는 항상 2로 생략됩니다.

1 - IEEE 754는 부호가있는 0 의 개념을 허용합니다. - +0-0 은 다르게 취급됩니다. 1 / (+0) 은 양의 무한대입니다. 1 / (-0) 은 음의 무한대입니다. 0 값의 경우, 가수 및 지수 비트는 모두 0입니다. 참고 : 0 값 (+0 및 -0)은 명시 적으로 비정상 2 로 분류되지 않습니다.

2 - 오프셋 지수가 0 인 비정규 숫자 (및 암시 된 0. )의 경우에는 해당되지 않습니다. 비정규 배정 밀도의 범위는 다음과 같습니다. d min ≤ | x | dmin (표현 가능한 가장 작은 0이 아닌 숫자)은 2 -1023 - 51 (≈ 4.94 * 10 - 324 )이고 d max (가수가 전체적으로 1 s로 구성된 최대 비정규 숫자)는 2 - 1023 + 1 - 2 -1023 - 51 (≈ 2.225 * 10 - 308 ).

배정도 수를 2 진수로 변환

이중 정밀도 부동 소수점 수를 binary ( binaryconvert.com )로 변환하는 많은 온라인 변환기가 있지만 여기에는 배정도 숫자에 대한 IEEE 754 표현을 얻기위한 샘플 C # 코드가 있습니다 (콜론 (:)으로 세 부분을 구분합니다) ) :

public static string BinaryRepresentation(double value)
{
    long valueInLongType = BitConverter.DoubleToInt64Bits(value);
    string bits = Convert.ToString(valueInLongType, 2);
    string leadingZeros = new string('0', 64 - bits.Length);
    string binaryRepresentation = leadingZeros + bits;

    string sign = binaryRepresentation[0].ToString();
    string exponent = binaryRepresentation.Substring(1, 11);
    string mantissa = binaryRepresentation.Substring(12);

    return string.Format("{0}:{1}:{2}", sign, exponent, mantissa);
}

요점에 도달하기 : 원래의 질문

(TL; DR 버전의 하단으로 건너 뛰기)

(질문 질문자)은 왜 0.1 + 0.2! = 0.3인지 질문했습니다.

이진 코드 (세 부분으로 구분 된 콜론 포함)로 작성된 IEEE 754 표현은 다음과 같습니다.

0.1 => 0:01111111011:1001100110011001100110011001100110011001100110011010
0.2 => 0:01111111100:1001100110011001100110011001100110011001100110011010

가수는 0011 의 되풀이 숫자로 구성됩니다. 이것은 계산에 오류가있는 이유의 핵심입니다. 0.1, 0.2 및 0.3은 한정된 수의 2 진 비트에서 정확히 1 / 9,1 / 3 또는 1/7 이상을 정확하게 2 진수로 나타낼 수 없습니다. 십진수 .

지수를 십진수로 변환하고 오프셋을 제거한 다음 암시 된 1 대괄호로 다시 추가하면 0.1과 0.2가됩니다.

0.1 = 2^-4 * [1].1001100110011001100110011001100110011001100110011010
0.2 = 2^-3 * [1].1001100110011001100110011001100110011001100110011010

두 개의 숫자를 더하려면 지수가 동일해야합니다.

0.1 = 2^-3 *  0.1100110011001100110011001100110011001100110011001101(0)
0.2 = 2^-3 *  1.1001100110011001100110011001100110011001100110011010
sum = 2^-3 * 10.0110011001100110011001100110011001100110011001100111

합이 2 n * 1. {bbb} 형식이 아니기 때문에 지수를 1 씩 증가시키고 10 진수 ( 2 진 ) 점을 이동시켜 다음과 같이 얻습니다.

sum = 2^-2 * 1.0011001100110011001100110011001100110011001100110011(1)

가수에는 53 비트가 있습니다 (53 번째 줄은 위 줄에 대괄호로 표시되어 있습니다). IEEE 754의 기본 반올림 모드 는 ' 가장 가까운 반올림 '입니다. 즉, 숫자 x 가 두 값 ab 사이에 있으면 최하위 비트가 0 인 값이 선택됩니다.

a = 2^-2 * 1.0011001100110011001100110011001100110011001100110011
x = 2^-2 * 1.0011001100110011001100110011001100110011001100110011(1)
b = 2^-2 * 1.0011001100110011001100110011001100110011001100110100

ab 는 마지막 비트에서만 다른 점 유의하십시오. ...0011 + 1 = ...0100 . 이 경우 최하위 비트가 0 인 값은 b 이므로 합계는 다음과 같습니다.

sum = 2^-2 * 1.0011001100110011001100110011001100110011001100110100

TL, DR

IEEE 754 바이너리 표현 (세 부분으로 분리 된 콜론)에 0.1 + 0.2 를 쓰고 이것을 0.3 과 비교하면 다음과 같습니다 (대괄호로 구분).

0.1 + 0.2 => 0:01111111101:0011001100110011001100110011001100110011001100110[100]
0.3       => 0:01111111101:0011001100110011001100110011001100110011001100110[011]

10 진수로 다시 변환 된 값은 다음과 같습니다.

0.1 + 0.2 => 0.300000000000000044408920985006...
0.3       => 0.299999999999999988897769753748...

차이는 정확히 2 -54 이며 ~ 5.5511151231258 × 10 -17 - 원래 값과 비교할 때 (많은 응용 프로그램에서) 중요하지 않습니다.

부동 소수점 숫자의 마지막 몇 비트를 비교하는 것은 본질적으로 위험합니다. 유명한 " 모든 컴퓨터 과학자가 부동 소수점 산술에 대해 알아야하는 것 "(이 답변의 모든 주요 부분을 다룹니다)을 읽는 사람은 누구나 알 수 있듯이 위험합니다.

대부분의 계산기는이 문제를 해결하기 위해 추가적인 보호 자릿수 를 사용합니다. 이는 0.1 + 0.20.3 을 얻는 방법입니다. 마지막 몇 비트는 반올림됩니다.


Math.sum (javascript) .... 연산자 교체

.1 + .0001 + -.1 --> 0.00010000000000000286
Math.sum(.1 , .0001, -.1) --> 0.0001
Object.defineProperties(Math, {
    sign: {
        value: function (x) {
            return x ? x < 0 ? -1 : 1 : 0;
            }
        },
    precision: {
        value: function (value, precision, type) {
            var v = parseFloat(value), 
                p = Math.max(precision, 0) || 0, 
                t = type || 'round';
            return (Math[t](v * Math.pow(10, p)) / Math.pow(10, p)).toFixed(p);
        }
    },
    scientific_to_num: {  // this is from https://gist.github.com/jiggzson
        value: function (num) {
            //if the number is in scientific notation remove it
            if (/e/i.test(num)) {
                var zero = '0',
                        parts = String(num).toLowerCase().split('e'), //split into coeff and exponent
                        e = parts.pop(), //store the exponential part
                        l = Math.abs(e), //get the number of zeros
                        sign = e / l,
                        coeff_array = parts[0].split('.');
                if (sign === -1) {
                    num = zero + '.' + new Array(l).join(zero) + coeff_array.join('');
                } else {
                    var dec = coeff_array[1];
                    if (dec)
                        l = l - dec.length;
                    num = coeff_array.join('') + new Array(l + 1).join(zero);
                }
            }
            return num;
         }
     }
    get_precision: {
        value: function (number) {
            var arr = Math.scientific_to_num((number + "")).split(".");
            return arr[1] ? arr[1].length : 0;
        }
    },
    diff:{
        value: function(A,B){
            var prec = this.max(this.get_precision(A),this.get_precision(B));
            return +this.precision(A-B,prec);
        }
    },
    sum: {
        value: function () {
            var prec = 0, sum = 0;
            for (var i = 0; i < arguments.length; i++) {
                prec = this.max(prec, this.get_precision(arguments[i]));
                sum += +arguments[i]; // force float to convert strings to number
            }
            return Math.precision(sum, prec);
        }
    }
});

아이디어는 float 오류를 피하기 위해 수학 대신 연산자를 사용하는 것입니다.

Math.diff(0.2, 0.11) == 0.09 // true
0.2 - 0.11 == 0.09 // false

또한 Math.diff와 Math.sum은 사용할 정밀도를 자동 감지합니다.

Math.sum은 여러 가지 인수를 허용합니다.


이 사실에 대한 응답으로 의도 된 이 질문 의 중복으로 폐쇄되었다 - 질문 하면서 내가 대신 여기에 게시합니다 ... 그래서 지금 내가 거기를 게시 할 수 없습니다, 내가 함께이 대답을 넣고 있었어요!

질문 요약 :

워크 시트에서 10^-8/100010^-11같이 평가 평등 VBA에서 그렇지 않은있다.

워크 시트에서 숫자는 기본값 인 과학 표기법을 사용합니다.

셀을 소수점 이있는 숫자 형식 ( Ctrl+ 1) 으로 변경하면 다음 Number과 같은 결과 15가 나타납니다.

=10^-11 returns 0.000000000010000
=10^(-8/1000) returns 0.981747943019984

따라서, 그들은 분명히 동일하지 않습니다 ... 하나는 거의 제로이고 다른 하나는 약 1입니다.

엑셀을 처리하도록 설계되지 않은 매우 작은 숫자 - 주식에 적어도하지에 설치합니다. 숫자 정밀도를 향상시키는 데 도움이되는 추가 기능이 있습니다.

Excel은 이진 부동 소수점 산술 ( IEEE 754 )에 대한 IEEE 표준에 따라 설계되었습니다 . 표준은 부동 소수점 숫자 가 저장되고 계산되는 방법을 정의합니다 . IEEE 754 가 있습니다 부동 때문에 소수점 숫자는 공간의 합리적인 금액에 저장하고 계산이 상대적으로 빠르게 일어날 수있는 표준이 널리 사용된다.

고정 소수점 표현에 비해 부동의 장점은 더 넓은 범위의 값을 지원할 수 있다는 것입니다. 예를 들어, 세 번째 자리 뒤에 배치 소수점 5 개 십진수를 갖는 고정 소수점 표현의 숫자를 나타낼 수 123.34, 12.23, 2.45등 부동 소수점 5 자리의 정밀도로 표현 나타낼 수있는 반면, 1.2345, 12345, 0.00012345 등 마찬가지로 부동 소수점 표현은 정밀도를 유지하면서 광범위한 범위의 계산을 허용합니다. 예를 들어,

기타 참고 문헌 :


내 해결 방법 :

function add(a, b, precision) {
    var x = Math.pow(10, precision || 2);
    return (Math.round(a * x) + Math.round(b * x)) / x;
}

정밀도 는 추가하는 동안 소수점 이후에 보존하려는 자릿수를 나타냅니다.


덕트 테이프 솔루션을 사용해 보셨습니까?

오류가 발생할 때를 결정하고 짧은 if 문을 사용하여 문제를 해결해보십시오. 문제가 해결되지 않았지만 일부 문제의 경우 유일한 해결 방법이며이 중 하나입니다.

 if( (n * 0.1) < 100.0 ) { return n * 0.1 - 0.000000000000001 ;}
                    else { return n * 0.1 + 0.000000000000001 ;}    

나는 C #의 과학 시뮬레이션 프로젝트에서 똑같은 문제를 겪었습니다. 나비 효과를 무시하면 커다란 뚱뚱한 용으로 변해서 **에서 당신을 물을 수 있다고 말할 수 있습니다 **


아무도 이것을 언급하지 않았다면 ...

Python과 Java와 같은 일부 고급 언어에는 이진 부동 소수점 제한을 극복 할 수있는 도구가 포함되어 있습니다. 예 :

  • 파이썬의 decimal모듈 과 Java의 BigDecimal클래스 . 십진수 표기법을 사용하여 내부적으로 숫자를 나타냅니다 (이진 표기법과 반대). 둘 다 정밀도가 낮기 때문에 오류가 발생하기 쉽지만 이진 부동 소수점 연산과 관련된 가장 일반적인 문제를 해결합니다.

    십진법은 돈을 다룰 때 매우 훌륭합니다. 10 센트와 20 센트는 항상 정확히 30 센트입니다 :

    >>> 0.1 + 0.2 == 0.3
    False
    >>> Decimal('0.1') + Decimal('0.2') == Decimal('0.3')
    True
    

    파이썬 decimal모듈은 IEEE 표준 854-1987을 기반으로 합니다.

  • 파이썬의 fractions모듈 과 아파치 코먼의 BigFraction클래스 . 둘 다 합리적인 숫자를 (numerator, denominator)쌍 으로 나타내며 십진법 부동 소수점 산술보다 더 정확한 결과를 제공 할 수 있습니다.

이러한 솔루션 중 어느 것도 완벽하지 않습니다 (특히 성능을 보거나 매우 높은 정밀도가 필요한 경우). 그러나 여전히 이진 부동 소수점 산술로 많은 문제를 해결합니다.


이 스레드는 현재 부동 소수점 구현에 대한 일반적인 토론으로 조금씩 분기했기 때문에 문제를 해결하는 프로젝트가 있다고 덧붙였습니다.

한 번 봐 가지고 https://posithub.org/ 적은 비트와 더 나은 정확성을 제공 할 것을 약속 멋 부리다 (및 그 이전의 Unum의)라는 숫자 유형을 전시 예를 들어,. 내 이해가 정확하다면 문제의 종류도 수정됩니다. 꽤 흥미로운 프로젝트, 그 뒤에있는 사람은 수학자 John Gustafson 입니다. 모든 것은 C / C ++, Python, Julia 및 C # ( https://hastlayer.com/arithmetics )의 많은 실제 구현과 함께 오픈 소스 입니다.


이 이상한 숫자는 컴퓨터가 계산 목적으로 2 진수 (2 진수) 시스템을 사용하고 10 진수 (10 진수)를 사용하기 때문에 나타납니다.

이진수 또는 십진수 또는 둘 다로 정확하게 표현할 수없는 부분 수의 대부분이 있습니다. 결과 - 반올림 된 (그러나 정확한) 숫자 결과.


이 질문과 중복되는 질문은 다음과 같습니다.

C ++에서 왜 cout << x디버거가 보여주는 값 과 다른 결과 가 나옵니까 x?

x질문은 float변수입니다.

한 가지 예가

float x = 9.9F;

디버거가 보여주는 9.89999962, cout작업 의 출력입니다 9.9.

대답은 cout기본 정밀도 float가 6이므로 6 자리로 반올림됩니다.

참조 정보는 here 를 참조하십시오.


Sine Python 3.5 math.isclose()if 조건에서 함수를 사용할 수 있습니다.

import math

if math.isclose(0.1 + 0.2, 0.3, abs_tol=0.01):
    pass

그냥 추가 할 수 있습니까? 사람들은 항상이 문제를 컴퓨터 문제라고 생각하지만, 당신이 손으로 계산하면 (기본 10), (1/3+1/3=2/3)=true무한대에 0.333 ... 0.333을 더하지 않으면 얻을 수 없습니다 . 그래서 (1/10+2/10)!==3/10기본 문제 와 마찬가지로 2, 0.333 + 0.333 = 0.666으로 자르면 0.667로 반올림됩니다. 기술적으로 부정확 할 수도 있습니다.

3 진수로 계산하면 3 분의 1은 문제가되지 않습니다. 어쩌면 각 손에 15 개의 손가락이있는 인종으로 인해 소수 수학이 왜 부러 졌는지 묻습니다.


부동 소수점 반올림 오류입니다. 에서 모든 컴퓨터 과학자는 부동 소수점 연산에 관한 숙지 사항 :

무한히 많은 실수를 제한된 수의 비트로 짜내는 것은 대략적인 표현을 필요로합니다. 무한히 많은 정수가 있지만 대부분의 프로그램에서 정수 계산의 결과는 32 비트로 저장할 수 있습니다. 반대로 고정 된 비트 수가 주어지면 실수를 사용하는 대부분의 계산은 해당 비트를 사용하여 정확히 표현할 수없는 양을 생성합니다. 따라서 부동 소수점 계산의 결과는 종종 유한 표현으로 되돌아 가기 위해 반올림되어야합니다. 이 반올림 오류는 부동 소수점 계산의 특징입니다.


이 질문의 다수는 특정 숫자에 대한 부동 소수점 올림 효과에 대해 많이 묻습니다. 실제로, 그것에 대해 읽는 것보다는 관심 계산의 정확한 결과를보고 어떻게 작동하는지에 대한 느낌을 얻는 것이 더 쉽습니다. 일부 언어는이를 수행하는 방법을 제공합니다 (예 : Java에서 a float또는 doubleto 변환) BigDecimal.

이것은 언어에 구애받지 않는 질문이기 때문에 Decimal to Floating-Point Converter 와 같은 언어에 구애받지 않는 도구가 필요합니다 .

그것을 두 배로 취급되는 질문의 숫자에 적용 :

0.1은 0.1000000000000000055511151231257827021181583404541015625로 변환되고,

0.2는 0.200000000000000011102230246251565404236316680908203125로 변환되고,

0.3은 0.299999999999999988897769753748434595763683319091796875로 변환되며,

0.30000000000000004는 0.3000000000000000444089209850062616169452667236328125로 변환됩니다.

처음 두 숫자를 수동으로 추가하거나 Full Precision Calculator 와 같은 십진 계산기 에 실제 입력 값의 정확한 합계가 0.3000000000000000166533453693773481063544750213623046875임을 보여줍니다.

해당 값을 0.3으로 반올림 한 경우 반올림 오류는 0.0000000000000000277555756156289135105907917022705078125가됩니다. 0.30000000000000004와 동등한 값으로 반올림하면 반올림 오류 0.0000000000000000277555756156289135105907917022705078125가 반환됩니다. 라운드 투 짝 타이 브레이커가 적용됩니다.

부동 소수점 변환기로 돌아가서 0.30000000000000004에 대한 원시 16 진수는 3fd3333333333334이며 짝수 숫자로 끝나며 올바른 결과입니다.





floating-accuracy