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


질문

 

다음 코드를 고려하십시오.

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

왜 이러한 부정확성이 일어나는가?


답변

 

이진 부동 소수점 수학은 이와 같습니다.대부분의 프로그래밍 언어에서는 IEEE 754 표준을 기반으로합니다.문제의 핵심은 숫자가 2의 힘의 정수 로이 형식으로 표현된다는 것입니다.분모가 2의 힘이 아닌 것은 정확히 표현 될 수없는 합리적인 숫자 (예 : 0.1, 1/10)를 정확하게 표현할 수 없습니다.

표준 바이너리 64 형식에서 0.1의 경우 표현은 정확하게 쓸 수 있습니다.

0.100000000000000005555111512312578270211815834021181583404541015620111562501562502015620

대조적으로, 1/10의 합리적인 번호 0.1은 정확하게 쓸 수 있습니다.

0.1 소수점, 또는

귀하의 프로그램에서 상수 0.2 및 0.3은 또한 진정한 가치에 근사치 될 것입니다.가장 가까운 이중 2 배의 0.2가 합리적으로 0.2보다 크지 만 가장 가까운 이중 0.3은 합리적으로 0.3보다 작였습니다.0.1과 0.2의 합은 합리적인 번호 0.3보다 커졌고, 따라서 귀하의 코드에서 상수와 동의하지 않음.

부동 소수점 산술 문제의 상당히 포괄적 인 치료는 모든 컴퓨터 과학자가 부동 소수점 산술에 대해 알아야하는 것입니다.더 쉽게 다이제 티브 설명을 위해 Floating-Point-Gui.de를 참조하십시오.

사이드 참고 : 모든 위치 (BASE-N) 번호 시스템은이 문제를 정확하게 공유합니다.

일반 십진수 (기본 10) 숫자는 동일한 문제가 있습니다. 이는 1/3가 0.33333333으로 끝나는 이유입니다 ...

십진수 시스템으로 표현하기 쉽지만 이진 시스템에 맞지 않는 숫자 (3/10)를 방금 뒤틀 렸습니다.1/16은 소수점 (0.0625)의 못생긴 숫자입니다 (0.0625)에서는 10,000 번째로 10,000th에서 깔끔하게 보입니다. ** - 우리가매일의 삶에서베이스 - 2 숫자 시스템을 사용하는 습관은 그 숫자를 살펴보고 본능적으로 당신이 뭔가를 반으로 줄이고, 다시 반복적으로 그것을 반으로 둘러싸고 다시 도착할 수 있습니다.

** 물론 부동 소수점 숫자가 메모리에 저장되는 방법 (과학 표기법의 형태로 사용)이 정확히 일치하지 않습니다.그러나 이진 부동 소유지 정밀 오류가 "실제 세계"번호가 우리가 일하는 것에 관심이있는 "실제 세계"수는 10 진수 시스템의 날을 사용하기 때문에 단지 핵심의 힘을 끌기 때문에 자르기 경향이있는 점을 보여줍니다.오늘.이것은 또한 "71 %마다 71 %"대신 71 %가 71 % "라고 말할 것입니다.

그래서 NO : 이진 부동 소수점 숫자가 손상되지 않아서 다른 모든 BAST-N 번호 시스템으로 불완전한 것처럼 발생합니다. :)

사이드 사이드 참고 : 프로그래밍에서 수레로 작업합니다

실제로이 정밀도의 문제는 반올림 함수를 사용하여 부동 소수점 숫자를 반올림하여 표시하기 전에 관심이있는 많은 소수 자리에 관심이 있습니다.

또한 평등 테스트를 약간의 공차를 허용하는 비교로 대체해야합니다.

if (x == y) {...}

대신 (ABS (x-y)

ABS는 절대 값입니다.MytoleranceValue는 특정 응용 프로그램을 위해 선택해야하며 허용 할 준비가되는 "Wiggle 룸"과 비교할 수있는 가장 큰 숫자가 (정밀도 손실로 인해) 할 수 있습니다.문제)."엡실론"스타일의 선택의 상수를 선택하십시오.이것들은 공차 값으로 사용되어서는 안됩니다.



답변

하드웨어 디자이너의 관점

나는 부동 소수점 하드웨어를 설계하고 빌드하기 때문에이를 위해 하드웨어 디자이너의 관점을 추가해야한다고 믿습니다.오류의 기원을 아는 것은 소프트웨어에서 일어나는 일을 이해하는 데 도움이 될 수 있으며 궁극적으로 나는 부동 소수점 오류가 발생하는 이유를 설명하는 데 도움이되기를 바랍니다.

1. 개요

엔지니어링 관점에서, 대부분의 부동 소수점 연산은 부동 소수점 계산을 수행하는 하드웨어가 마지막 위치에서 한 단위의 절반 미만의 오류가 있기 때문에 일부 오류 요소가 있습니다.따라서 많은 하드웨어가 부유 지점 부서에서 특히 문제가되는 단일 작업을 위해 마지막 작업의 한 단위의 절반 미만의 오차를 산출하는 데 필요한 정밀도로 멈출 것입니다.단일 작업을 구성하는 것은 단위가 얼마나 많은 피연산자가 걸리는지에 따라 다릅니다.대부분의 경우 두 가지이지만 일부 단위는 3 개 이상의 피연산자를 섭취합니다.이 때문에 오류가 시간이 지남에 따라 추가되므로 반복되는 작업이 원하는 오류가 발생할 수있는 보장이 없습니다.

2. 표준

대부분의 프로세서는 IEEE-754 표준을 따르지 만 일부는 디스코이션 또는 다른 표준을 사용합니다. ...에예를 들어, 정밀도를 희생시켜 매우 작은 부동 소수점 숫자를 표현할 수있는 IEEE-754에는 탈지 화 된 모드가 있습니다.그러나 다음은 일반적인 작동 모드 인 IEEE-754의 정규화 된 모드를 다루게됩니다.

IEEE-754 표준에서는 하드웨어 설계자가 마지막 장소에서 한 단위의 절반보다 작 으면 오류 / 엡실론의 값을 허용하고 결과는 마지막으로 한 단위의 절반 미만이어야합니다.하나의 조작을위한 장소.이는 반복 된 작업이 있으면 오류가 추가되는 이유를 설명합니다.IEEE-754 이중 정밀도의 경우, 53 비트가 부동 소수점 수 (예를 들어 5.3E5의 5.3)의 Mantissa라고도하는 숫자 부품 (정규화)이라고도하는 숫자 부품 (정규화 된)을 나타내는 데 사용되므로 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 부서와 같은 빠른 분할과 같은 느린 부문에 의존하는 것에 의존한다. 각 항목은 가능한 가장 낮은 오류를 생성하려는 시도에서 분할 알고리즘에 따라 수정됩니다. 어쨌든 모든 관찰자는 실제 상호 종로의 근사치이며 일부 오류 요소를 도입합니다. Slow Division 및 Fast Division 메소드는 몫을 반복적으로 계산합니다. 즉, 몫의 몇 비트가 각 단계에서 계산되면 결과가 배당금에서 뺍니다. 오류가 한쪽 절반 미만이 될 때까지 단계를 반복합니다. 마지막 장소의 유닛. Slow Division 메서드는 각 단계에서 몫의 고정 된 자릿수를 계산하고 일반적으로 빌드 비용이 적고 빠른 분할 방법은 단계별로 가변 숫자 수를 계산하고 일반적으로 빌드하는 데 더 비쌉니다. 분할 방법의 가장 중요한 부분은 상호의 근사치에 의해 반복적 인 곱셈시 대부분의 경우에 오류가 발생하기 쉽습니다.

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

모든 작업에서 반올림 오류의 또 다른 원인은 IEEE-754가 허용하는 최종 답변의 다른 절단 모드입니다.둥근쪽으로, 둥근 가장 가까운 (기본값), 반올림 및 반올림이 있습니다.모든 방법은 단일 작업을위한 마지막 위치에서 하나 이상의 단위의 오류 요소를 도입합니다.시간과 반복되는 작업을 통해 절단은 결과 오류에 누적 적으로 추가됩니다.이 절단 오차는 특히 반복되는 곱셈의 일부 형태를 포함하는 지수에서 특히 문제가됩니다.

5. 반복되는 작업

부동 소수점 계산을 수행하는 하드웨어는 마지막 작업의 마지막 위치에서 한쪽 단위 중 절반 미만의 오류가 발생하여 결과를 생성해야하므로 보지 않으면 반복되는 작업을 통해 오류가 커집니다.이는 경계 된 오류가 필요한 계산에서 수학자가 시간이 지남에 따라 IEEE-754의 마지막 장소에서 가장 가까운 심지어 자릿수를 사용하는 것과 같은 방법을 사용하는 이유입니다.Out 및 Interval 산술은 반올림 오류를 예측하고이를 수정하기 위해 IEEE 754 반올림 모드의 변형과 결합되었습니다.다른 반올림 모드와 비교하여 낮은 상대적인 오류가 발생했기 때문에 가장 가까운 짝수 (마지막 위치에서)는 IEEE-754의 기본 반올림 모드입니다.

마지막 위치에서 기본 반올림 모드 인 균등 한 숫자는 하나의 작업을 위해 마지막 위치에서 한 단위 중 절반 미만의 오류를 보장합니다.절단, 반올림 및 반올림을 사용하여 마지막 위치에서 한 단위 중 절반 이상이지만 마지막 위치에서 한 단위보다 작은 오류가 발생할 수 있으므로 이러한 모드는간격 산술에 사용됩니다.

6. 요약

즉, 부동 소수점 연산의 오류의 기본 이유는 하드웨어의 절단과 부서의 경우 상호 배역의 절단입니다.IEEE-754 표준만이 마지막 작업의 한 단위 중 절반 미만의 오차 만 필요로하므로 수정되지 않는 한 반복되는 작업을 통한 부동 소수점 오류가 추가됩니다.



답변

똑같은 방식으로 똑같은 방식으로 깨졌습니다. 당신이 학년 학교에서 배운 표기법은 Base-2 만 해당합니다.

이해하기 위해 1/3을 십진수 값으로 나타내는 것에 대해 생각해보십시오.정확하게하는 것은 불가능합니다!같은 방식으로, 1/10 (십진수 0.1)은베이스 2 (2 진)에서 정확히 "소수점"값으로 표시 될 수 없습니다.소수점 이후의 반복 패턴은 영원히 계속됩니다.값은 정확하지 않으므로 정상적인 부동 소수점 방법을 사용하여 정확한 수학을 수행 할 수는 없습니다.



답변

대부분의 답변은이 질문을 매우 건조하고 기술적 인 용어로 해결합니다.정상적인 인간이 이해할 수있는 측면 에서이 문제를 해결하고 싶습니다.

피자를 조각하려고한다고 상상해보십시오.피자 조각을 정확히 절반으로자를 수있는 로봇 피자 커터가 있습니다.그것은 전체 피자를 반으로 줄일 수 있거나 기존 슬라이스를 절반을 줄일 수 있지만 어떤 경우에도 반으로는 항상 정확합니다.

피자 절단기는 매우 훌륭한 움직임을 가지고 있으며, 전체 피자로 시작하면, 매번 가장 작은 슬라이스를 계속 반으로 줄이면 슬라이스가 너무 작아서 조각이 너무 작아서 고정밀도 능력조차도 슬라이스가 너무 작습니다....에이 시점에서 더 이상 매우 얇은 슬라이스를 반으로 줄 수는 없지만 그대로 포함하거나 제외해야합니다.

이제는 피자의 1/10 (0.1) 또는 5/5 (0.2)을 추가하는 방식으로 모든 조각을 어떻게 조각합니까?정말로 그것에 대해 생각하고 그것을 밖으로 일해보십시오.신화의 정밀 피자 커터가있는 경우 실제 피자를 사용하려고 할 수도 있습니다.:-)


대부분의 숙련 된 프로그래머는 물론 진정한 답변을 알고 있으며 그 조각을 사용하여 피자의 정확한 열 번째 또는 다섯 번째로 피자를 조각 할 수있는 방법이 아닙니다.당신은 꽤 좋은 근사치를 할 수 있으며, 0.2의 근사치로 0.1의 근사치를 합산하는 경우, 당신은 0.3의 꽤 좋은 근사치를 얻었지만, 그것은 아직도 근사치입니다.

후자는 전자보다 0.1보다 약간 더 가깝기 때문에 0.1의 입력이 주어지면서 숫자 파서가 후자를 선호합니다.

(이 두 숫자의 차이점은 상향 바이어스를 도입하거나 하향 바이어스를 도입 한 것으로 결정 해야하는 "가장 작은 슬라이스"입니다. 가장 작은 슬라이스에 대한 기술적 기간은 ULP입니다.)

0.2의 경우, 숫자는 모두 동일하며, 즉 2 배로 확장되면, 다시 0.2보다 약간 높이는 값을 선호합니다.

두 경우 모두 0.1 및 0.2의 근사치는 약간의 위쪽으로 바이어스가 있습니다.이러한 편향을 충분히 추가하면 숫자를 우리가 원하는 것과 더 멀리 멀리 멀어 질 것입니다. 실제로 0.1 + 0.2의 경우 바이어스는 결과 숫자가 더 이상 가장 가까운 숫자가 아닙니다.0.3까지.

In particular, 0.1 + 0.2 is really 0.1000000000000000055511151231257827021181583404541015625 + 0.200000000000000011102230246251565404236316680908203125 = 0.3000000000000000444089209850062616169452667236328125, whereas the number closest to 0.3 is actually 0.299999999999999988897769753748434595763683319091796875.


추신일부 프로그래밍 언어는 조각을 정확한 10 분야로 분할 할 수있는 피자 커터도 제공합니다.이러한 피자 절단기는 드문 일이지만, 하나에 액세스 할 수 있다면, 정확히 1/10 또는 1/5의 슬라이스를 얻을 수있는 것이 중요 할 때 사용해야합니다.

(원래 쿼라에 게시 됨).



답변

부동 소수점 반올림 오류.0.1은 1/3의 누락 된 소수 요소로 인해베이스 -10에서와 같이베이스 -2에서 정확하게 표현 될 수 없습니다. 1/3은 십진수로 표시 할 수있는 무한 숫자가 있지만 Base-3에서 "0.1"입니다.0.1베이스 -10이 아닌 Base-2에서 무한 숫자 수를 취합니다.컴퓨터에는 무한한 양의 메모리가 없습니다.



답변

내 대답은 꽤 오래이므로 3 개의 섹션으로 나눕니다.질문은 부동 소수점 수학에 관한 것이기 때문에 기계가 실제로하는 것에 중점을두고 있습니다.나는 또한 이중 (64 비트) 정밀도로 특정하게 만들었지 만, 논쟁은 부동 소수점 산술에 동일하게 적용됩니다.

전문

IEEE 754 이중 정밀 바이너리 부동 소수점 형식 (Binary64) 번호는 수의 수를 나타냅니다.

값 = (-1) ^ s * (1.m51m50 ... m2m1m0) 2 * 2E-1023

64 비트에서 :

첫 번째 비트는 부호 비트입니다. 1 숫자가 음수이면 0이 아니더라도 0입니다. 다음 11 비트는 1023 년까지 오프셋 된 지수입니다. 즉, 이중 정밀도 번호로부터 지수 비트를 읽은 후에는 1023을 뺀 값을 빼고 두 개의 힘을 얻으십시오. 나머지 52 비트는 유효성이 있거나 (또는 Mantissa)입니다.MANTISSA에서 '묵시적'1. 모든 이진 값의 가장 중요한 비트가 1이기 때문에 항상 생략되어 있습니다.

1 - IEEE 754가 부호가있는 0 - +0 및 -0의 개념을 다르게 처리 할 수있게합니다. 1 / (+0)은 긍정적 인 무한대입니다.1 / (-0)은 음의 무한대입니다.0 값의 경우, MANTISSA 및 Enconent 비트는 모두 0입니다.참고 : 0 값 (+0 및 -0)은 명시 적으로 Denormal2로 분류되지 않습니다.

2 - 이것은 0의 오프셋 지수가 0의 오프셋 지수를 갖는 (및 묵시적 0)의 경우의 경우가 아닙니다.Denormal Double Precision Numbers의 범위는 dmin ≤ | x |≤ DMAX (가장 작은 표현 가능한 0이 아닌 수)는 2-1023 - 51 (△ 4.94 * 10-324) 및 DMAX (mantissa가 완전히 1S로 구성된 가장 큰 유역 번호)가 2-1023 + 1입니다.2-1023 - 51 (2.225 * 10-308).


이중 정밀도를 바이너리로 돌리십시오

많은 온라인 변환기가 두 배 정밀도 부동 소수점 번호를 바이너리 (예 : 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 버전)

CATO Johnston (질문 asker)은 0.1 + 0.2! = 0.3 이유를 물었습니다.

2 진 (3 부분을 분리하는 콜론으로)으로 작성된 IEEE 754 값의 표현은 다음과 같습니다.

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

Mantissa는 0011의 반복 자릿수로 구성됩니다. 이는 계산에 대한 오류가있는 이유의 핵심입니다 - 0.1, 0.2 및 0.3은 1/9 이상의 바이너리 비트 수에서 정확하게 정확하게 표현 될 수 없으며,1/3 또는 1/7은 십진수로 정확하게 표현할 수 있습니다.

또한 우리는 지수에서 52까지의 전력을 줄이고 바이너리 표현의 지점을 오른쪽으로 52 개 (10-3 * 1.23 == 10-5 * 123과 마찬가지로) 이동할 수 있습니다.그런 다음 이는 2 진 표현을 A * 2P 양식에서 나타내는 정확한 값으로 표현할 수 있습니다.'a'는 정수입니다.

지수를 소수점으로 변환하고, 오프셋을 제거하고, 묵시적 1 (대괄호 안에), 0.1 및 0.2에서 묵시적 1 (정사각형 브래킷)을 다시 추가하는 것은 다음과 같습니다.

0.1 => 2^-4 * [1].1001100110011001100110011001100110011001100110011010
0.2 => 2^-3 * [1].1001100110011001100110011001100110011001100110011010
or
0.1 => 2^-56 * 7205759403792794 = 0.1000000000000000055511151231257827021181583404541015625
0.2 => 2^-55 * 7205759403792794 = 0.200000000000000011102230246251565404236316680908203125

두 개의 숫자를 추가하기 위해 지수가 동일해야합니다. i.e.

0.1 => 2^-3 *  0.1100110011001100110011001100110011001100110011001101(0)
0.2 => 2^-3 *  1.1001100110011001100110011001100110011001100110011010
sum =  2^-3 * 10.0110011001100110011001100110011001100110011001100111
or
0.1 => 2^-55 * 3602879701896397  = 0.1000000000000000055511151231257827021181583404541015625
0.2 => 2^-55 * 7205759403792794  = 0.200000000000000011102230246251565404236316680908203125
sum =  2^-55 * 10808639105689191 = 0.3000000000000000166533453693773481063544750213623046875

합계는 2N * 1의 형식이 아니기 때문에 우리는 지수를 하나씩 늘리고 십진수 (2 진수) 지점을 이동시키기 위해 시프트합니다.

sum = 2^-2  * 1.0011001100110011001100110011001100110011001100110011(1)
    = 2^-54 * 5404319552844595.5 = 0.3000000000000000166533453693773481063544750213623046875

이제는 MANTISSA에 53 비트가 있습니다 (53RD는 위의 라인에서 대괄호 안에 있습니다).IEEE 754의 기본 반올림 모드는 '가장 가까운 것으로 둥글다'- I.E..e. 숫자 x가 두 값 A와 B 사이에 떨어지면 최하위 비트가 0이되는 값이 선택됩니다.

a = 2^-54 * 5404319552844595 = 0.299999999999999988897769753748434595763683319091796875
  = 2^-2  * 1.0011001100110011001100110011001100110011001100110011

x = 2^-2  * 1.0011001100110011001100110011001100110011001100110011(1)

b = 2^-2  * 1.0011001100110011001100110011001100110011001100110100
  = 2^-54 * 5404319552844596 = 0.3000000000000000444089209850062616169452667236328125

A와 B는 마지막 비트에서만 다릅니다.... 0011 + 1 = ... 0100.이 경우 0의 최하위 비트가있는 값은 B이므로 합계는 다음과 같습니다.

sum = 2^-2  * 1.0011001100110011001100110011001100110011001100110100
    = 2^-54 * 5404319552844596 = 0.3000000000000000444089209850062616169452667236328125

0.3의 이진 표현은 다음과 같습니다.

0.3 => 2^-2  * 1.0011001100110011001100110011001100110011001100110011
    =  2^-54 * 5404319552844595 = 0.299999999999999988897769753748434595763683319091796875

0.1 및 0.2 × 2-54의 합의 2 진 표현과 만 다릅니다.

0.1 및 0.2의 바이너리 표현은 IEEE 754가 허용하는 숫자의 가장 정확한 표현입니다. 기본 반올림 모드로 인해 이러한 표현을 추가하면 최소한의 중요한 비트에서만 다른 값이 다릅니다.

TL; 박사

IEEE 754 바이너리 표현에 0.1 + 0.2를 기입하십시오 (세 부분을 분리하는 콜론으로) 0.3과 비교하면 (나는 별개의 비트를 사각형 브래킷에 넣었다).

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

다시 소수점으로 변환,이 값은 다음과 같습니다.

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

그 차이는 원래 값과 비교할 때 ~ 5.5511151231258 × 10-17 - 중요하지 않은 경우 정확히 2-54입니다.

부동 소수점 숫자의 마지막 몇 비트를 비교하는 것은 유명한 "모든 컴퓨터 과학자에 대해 알아야 할 모든 컴퓨터 과학자 가이 답변의 모든 주요 부분을 다루는 것"(이 답변의 모든 주요 부분을 다루는 모든 컴퓨터 과학자 "를 읽는 사람이 알고 있습니다.

대부분의 계산기는이 문제를 해결하기 위해 추가 가드 숫자를 사용합니다.이 문제는 0.1 + 0.2가 0.3 : 마지막 거의 비트가 둥글게되는 방법입니다.

출처:https://stackoverflow.com/questions/588004/is-floating-point-math-broken