플로트 부정확성으로 인한 불평등


15

적어도이 코드를 작성하면 Java에서 :

float a = 1000.0F;
float b = 0.00004F;
float c = a + b + b;
float d = b + b + a;
boolean e = c == d;

의 값 것이 . 나는 이것이 수를 정확하게 표현하는 방식으로 부동 소수점이 매우 제한되어 있기 때문에 발생한다고 생각합니다. 단지의 위치 변화하지만 왜 이해가 안 이 불평등을 야기 할 수 있습니다.이자형에프에스이자형

I는 감소 모두 선로 (3)에 하나의 값을 아래와 같이 4 (S)을 그러나된다 :e t r u e이자형아르 자형이자형

float a = 1000.0F;
float b = 0.00004F;
float c = a + b;
float d = b + a;
boolean e = c == d;

3 호선과 4 호선에서 정확히 무슨 일이 있었습니까? float를 사용한 덧셈 연산이 연관성이없는 이유는 무엇입니까?

미리 감사드립니다.


16
예제에서 알 수 있듯이 부동 소수점 추가 정식입니다. 그러나 그것은 연관성이 없습니다.
Yuval Filmus

1
기본 정의를 찾아 보는 것이 좋습니다. 컴파일러는 ( r + s ) + t (추가는 왼쪽에 연결 ) 로 구문 분석 합니다. 아르 자형+에스+(아르 자형+에스)+
Yuval Filmus

2
이것이 왜 그렇게되어야 하는지를 쉽게 알 수있게하려면 X매우 큰 수와 Y매우 작은 수를 고려 하십시오 X + Y = X. 여기서는 X + Y + -X0이됩니다. 그러나 X + -X + Y될 것 Y입니다.
David Schwartz


답변:


20

일반적인 부동 소수점 구현에서 단일 연산의 결과는 마치 연산이 무한정 정밀하게 수행 된 것처럼 생성 된 다음 가장 가까운 부동 소수점 수로 반올림됩니다.

비교 +의 B를 하고 , B + : 무한의 정밀도로 수행 각 연산의 결과는 동일하므로이 무한 정밀 결과는 동일한 방식으로 반올림 동일하다. 즉, 부동 소수점 추가는 정식입니다.a+bb+a

테이크 : B는 부동 소수점 숫자입니다. 함께 이진 부동 소수점 수, (2) B는 이렇게 부동 소수점 수 (지수가 하나 더 크다), 또한 B + B는 반올림 오차없이 추가된다. 그런 다음 a정확한b + b에 추가됩니다 . 결과는 가장 가까운 부동 소수점 수로 반올림 된 정확한2 b + a 입니다.b+b+ab2bb+bab+b2b+a

가지고 + B + B : + B 것은 추가되고, 반올림 오류가있을 것 R 우리는 결과를 얻을 수 있도록, + B + R을 . 추가 B를 , 그 결과는이다 정확한B + + R은 가장 가까운 부동 소수점 수로 반올림.a+b+ba+bra+b+rb2b+a+r

하나의 경우, 는 반올림됩니다. 다른 경우에, 2 (B) + + R , 반올림.2b+a2b+a+r

추신. 두 개의 특정 숫자 b에 대해 두 계산 모두 동일한 결과를 제공 하는지 여부 는 숫자와 계산 a + b 의 반올림 오차에 따라 달라지며 일반적으로 예측하기 어렵습니다. 단정도 또는 배정도를 사용하면 원칙적으로 문제와 차이가 없지만 반올림 오차가 다르기 때문에 단 정밀도에서 결과가 같고 배정도에서는 같지 않으며 그 반대의 경우 a와 b의 값이 있습니다. 정밀도는 훨씬 높아지지만 두 표현식이 수학적으로 동일하지만 부동 소수점 산술에서 동일하지 않다는 문제는 동일하게 유지됩니다.aba+b

PPS. 일부 언어에서는 부동 소수점 산술이 실제 명령문에서 제공하는 것보다 높은 정밀도 또는 더 높은 수의 범위로 수행 될 수 있습니다. 이 경우 두 합계가 동일한 결과를 제공 할 가능성이 훨씬 높습니다 (그러나 여전히 보장되지는 않음).

PPPS. 한 의견은 부동 소수점 숫자가 같은지 물어볼 것인지 물었다. 당신이하고있는 일을 알고 있다면 절대적으로. 예를 들어, 배열을 정렬하거나 집합을 구현하는 경우 "대략 같은"개념을 사용하려는 경우 심각한 문제에 봉착하게됩니다. 그래픽 사용자 인터페이스에서 객체의 크기가 변경된 경우 객체 크기를 다시 계산해야 할 수도 있습니다. oldSize == newSize를 비교하여 재 계산을 피하고 실제로는 거의 동일한 크기를 가지고 있지 않으며 프로그램이 올바른지 알고 불필요한 재 계산이 있더라도.


이 특정 경우, b는 이진수로 변환 될 때 주기적이되므로 모든 곳에서 반올림 오류가 발생합니다.
André Souza Lemos

1
b이 답변의 @ AndréSouzaLemos 는 0.00004가 아니며 변환 및 반올림 얻는 입니다.
Alexey Romanov

"일반적인 부동 소수점 구현에서 단일 연산의 결과는 마치 연산이 무한정 정밀도로 수행 된 것처럼 생성 된 다음 가장 가까운 부동 소수점 수로 반올림됩니다."-실제로 사양에 따라 달라집니다. 로직 게이트 측면에서 실제로 구현하려고 시도했을 때 (시뮬레이터는 64 비트 버스 만 처리 할 수있었습니다).
John Dvorak

순진한 질문 : 부동 평등 테스트가 의미가 있습니까? 왜 대부분의 프로그래밍 언어에서 둘 다 또는 하나가 부동 소수점 인 aa == b 테스트를 허용합니까?
curious_cat

위키피디아의 관련 정의 : " 머신 엡실론 은 부동 소수점 산술의 반올림으로 인한 상대 오차의 상한을 제공합니다."
Blackhawk

5

컴퓨터가 지원하는 이진 부동 소수점 형식은 본질적으로 사람이 사용하는 십진법 표기법과 유사합니다.

부동 소수점 숫자는 다음과 같이 부호, 가수 (고정 너비) 및 지수 (고정 너비)로 구성됩니다.

+/-  1.0101010101 × 2^12345
sign   ^mantissa^     ^exp^

일반적인 과학 표기법은 비슷한 형식입니다.

+/- 1.23456 × 10^99

우리가 과학적 표기법으로 유한 정밀도로 산술을 수행하면 각 연산 후에 반올림하면 이진 부동 소수점과 동일한 나쁜 효과가 발생합니다.


설명하기 위해 소수점 다음에 정확히 3 자리를 사용한다고 가정하십시오.

a = 99990 = 9.999 × 10^4
b =     3 = 3.000 × 10^0

(a + b) + b

이제 우리는 다음을 계산합니다.

c = a + b
  = 99990 + 3      (exact)
  = 99993          (exact)
  = 9.9993 × 10^4  (exact)
  = 9.999 × 10^4.  (rounded to nearest)

다음 단계에서 물론 :

d = c + b
  = 99990 + 3 = ...
  = 9.999 × 10^4.  (rounded to nearest)

따라서 (a + b) + b = 9.999 × 10 4 입니다.

(b + b) + a

그러나 다른 순서로 작업을 수행 한 경우 :

e = b + b
  = 3 + 3  (exact)
  = 6      (exact)
  = 6.000 × 10^0.  (rounded to nearest)

다음으로 계산합니다 :

f = e + a
  = 6 + 99990      (exact)
  = 99996          (exact)
  = 9.9996 × 10^4  (exact)
  = 1.000 × 10^5.  (rounded to nearest)

따라서 (b + b) + a = 1.000 × 10 5 이며 이는 다른 답변과 다릅니다.


5

Java는 IEEE 754 이진 부동 소수점 표현을 사용합니다.이 이진 부동 소수점 표현은 가수에 23 개의 이진수를 표시하며, 첫 번째 유효 숫자 (공간을 절약하기 위해 생략)로 시작하도록 정규화됩니다.

0.0000410=0.00000000000000101001111100010110101100010001110001101101000111 ...2=[1.]01001111100010110101100010001110001101101000111 ...2×215

100010+0.0000410=1111101000.00000000000000101001111100010110101100010001110001101101000111 ...2=[1.]11110100000000000000000101001111100010110101100010001110001101101000111 ...2×29

빨간색으로 표시된 부분은 실제로 표현 된 반올림 전에 가수입니다.

(100010+0.0000410)+0.0000410(0.0000410+0.0000410)+100010


0

최근 비슷한 반올림 문제가 발생했습니다. 위에서 언급 한 답변은 정확하지만 매우 기술적입니다.

반올림 오류가 존재하는 이유에 대해 다음이 좋은 설명임을 알았습니다. http://csharpindepth.com/Articles/General/FloatingPoint.aspx

TLDR : 이진 부동 소수점을 소수점 부동 소수점에 정확하게 매핑 할 수 없습니다. 이로 인해 수학 연산 중에 부정확 한 결과가 발생할 수 있습니다.

10 진수 부동 숫자를 사용하는 예 : 1/3 + 1/3 + 1/3은 일반적으로 1과 같습니다. 그러나 10 진수 : 0.333333 + 0.333333 + 0.333333은 절대 정확히 1.000000과 같습니다.

이진수에 대해 수학 연산을 수행 할 때도 마찬가지입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.