15. 부동 소수점 산술:문제 및 제한¶

부동 소수점 숫자는 기본 2(이진)분수로 컴퓨터 하드웨어에 표시됩니다. 예를 들어,소수 부분

0.125

은 값 1/10 + 2/100 + 5/1000 과 같은 방법으로는 바이너리 부분

0.001

은 값 0/2 + 0/4 + 1/8. 이러한 두 개의 분수가 동일한 값 onlyreal 차이점은 처음에 기록된 기준 10 개 소수 표기와 두 번째에 기초 2.

불행히도 대부분의 소수 분수는 binaryfractions 로 정확하게 나타낼 수 없습니다. 결과는 일반적으로 입력하는 십진수 부동 소수점 숫자 만 근사합니다.실제로 기계에 저장됩니다.문제는 기본 10 에서 처음에는 이해하기 쉽습니다. 분수를 고려하십시오.1/3. 기본 10 분수로 근사할 수 있습니다:

0.3

거나,더 나아가

0.33

거나,더 나은,

0.333

와. 아무리 많은 숫자를 적어도결과는 정확히 1/3 이 아니지만 1/3 의 점점 더 나은 근사치가 될 것입니다.

에서 같은 방법으로,아무리 많은 기 2 자리 숫자로 기꺼이 자신을 사용하여,thedecimal0.1 값은 표현할 수 없을 정확히 기반으로 2 일부에 지나지 않는다는 것을. 에 base2,1/10 년의 무한 반복되는 부분

0.0001100110011001100110011001100110011001100110011...

중지에서 모든 유한 비트의 개수를,그리고 당신은 근사입니다. 에 mostmachines 오늘날,수레가 근사화를 사용하여 바이너리 부분을 가진 분자 사용하는 첫 번째 53 비트로 시작하는 가장 중요한 비트 andwith 분모로 두 개의 전원. 1/10 의 경우 이진 fractionis3602879701896397 / 2 ** 55는 1/10 의 진정한 값에 가깝지만 정확하게 동일하지 않습니다.

많은 사용자가 값이 표시되는 방식 때문에 근사치를 인식하지 못합니다. Python 은 기계에 의해 저장된 이진 근사치의 진정한 decimalvalue 에 대한 10 진수 근사치 만 인쇄합니다. 대부분의 시스템에서 ifpython 은 이진 근사 storedfor0 의 진정한 십진수 값을 인쇄해야했습니다.1 것을 표시해야

>>> 0.10.1000000000000000055511151231257827021181583404541015625

는 숫자보다는 대부분의 사람들이 찾을 수용,그래서 Python 유지의 수 숫자 관리 표시하여 둥근 값을 대신

>>> 1 / 100.1

기억, 지 인쇄 결과는 다음과 같은 정확한 valueof1/10,실제 저장된 값은 표현할 수 있는 가장 가까운 바이너리 부분이다.

흥미롭게도,동일한 대략적인 이진 분수를 공유하는 많은 다른 십진수가 있습니다. 예를 들어,숫자0.10.100000000000000010.1000000000000000055511151231257827021181583404541015625는 allapproximated 에 의해3602879701896397 / 2 ** 55. 이 모든 decimalvalues 는 동일한 근사값을 공유하므로 불변eval(repr(x)) == x를 유지하면서 둘 중 하나를 표시 할 수 있습니다.

역사적으로,Python 프롬프트 내장repr()0.10000000000000001. Python3 으로 시작합니다.1,Python(대부분의 시스템에서)은 이제 가장 짧은 것을 선택하고 단순히0.1를 표시 할 수 있습니다.이것은 바이너리 부동 소수점의 본질에 있습니다:이것은 bugin Python 이 아니며 코드에서도 버그가 아닙니다. 보는 것과 동일한 종류 ofthing 모든 언어로 지원하는 하드웨어 부동 소수점 연산(지만 몇 가지 언어로 표시되지 않을 수 있습니다 기본적으로 차이 또는 alloutput 모드).

보다 쾌적한 산출할 수 있습니다 사용할 문자열 형식을 생산하는 제한된 수의 뜻깊은 자리:이것이 실제 의미에서 환상이라는 것을 깨닫는 것이 중요합니다:실제 기계 값의 표시를 반올림합니다.

하나의 환상이 다른 환상을 낳을 수 있습니다. 예를 들어,이후 0.1 지 정확히 1/10,요약 세 값의 0.1 지 않을 수도 있 수익률이 정확히 0.3 다음 중 하나를 수행합니다.

>>> .1 + .1 + .1 == .3False

또한,이후 0.1 얻을 수 없습니다 어떤 가까이 정확한 값을 1/10and0.3 얻을 수 없습니다 어떤 가까이 정확한 값을 3/10,다음 pre-반올림과 함께round()기능을 도울 수 없:

>>> round(.1, 1) + round(.1, 1) + round(.1, 1) == round(.3, 1)False

지 번호는 만들 수 없습니다 가까운 그들의 의도한 정확한 값을round()기능이 유용할 수 있는 포스트-반올림하도록 resultswith 부정확한 값은 비교 한다:

>>> round(.1 + .1 + .1, 10) == round(.3, 10)True

바이너리 부동 소수점 연산을 보유하고 많은 놀라움이 이와 같습니다. 문제는”0.1″은”표현 오류”섹션에서 아래에 자세히 설명되어 있습니다. 떠 다니는 점의 위험을보십시오.다른 일반적인 놀라움의 더 완전한 계정을 위해.

끝 부분에서 말한 것처럼”쉬운 대답은 없습니다.”아직도,부동 소수점의 undulwary 하지 마십시오! Python float 연산의 오류는 상속됩니다.부동 소수점 하드웨어에서 대부분의 시스템에서 작업 당 2**53 에서 1 부분보다 nomore 의 순서에 있습니다. 는 더 이상의 적절한 mosttasks,하지만 당신은 당신을 유지해야 하는 마음에 그것은 소수점 연산 andthat 모든 작업 부동할 수 있는 고통을 새로운 반올림 오류가 있습니다.

는 동안 병리학적인 케이스가 존재 대부분의 캐주얼의 사용을 떠-pointarithmetic 당신은 결과는 당신이 기대에서 끝나는 경우에 당신은 단순히 라운드 표시 당신의 최종 결과를 수의 숫자를 기대합니다.str()str.format()방법의 형식을 지정자로서 형식 문자열을 구문입니다.

사용을 위해 필요한 경우 정확한 숫자 표현이 사용하십시오decimal모듈이 구현하는 소수점 연산에 적합 foraccounting 응용 프로그램과 높은 정밀 응용 프로그램.

또 다른 형태의 정확한 산술 지원fractionsmodulewhich 구현산에 따라 합리적인 숫자는(그렇기에 숫자는 like1/3 개의 표현할 수 있는 정확하게).

경우 사용자의 부동 소수점 연산을 수행해야 합 두 숫자 Python 패키지 그리고 많은 다른 패키지에 대한 수학적 andstatistical 작업에 의해 공급되 SciPy 프로젝트입니다. 나는 이것이 어떻게 작동하는지 잘 모르겠습니다.

Python 은 플로트의 정확한 값을 실제로 알고 싶을 때 드문 경우에 도움이 될 수있는 도구를 제공합니다. float.as_integer_ratio()메서드는 float 의 값을 afraction 으로 표현합니다:

>>> x = 3.14159>>> x.as_integer_ratio()(3537115888337719, 1125899906842624)

이후 비율 정확한 사용할 수 있습을 losslessly 다시 원래 값:

>>> x == 3537115888337719 / 1125899906842624True

float.hex()방법을 표현하는 부유물에서 진수(base16),또는 정확한 값을 저장해 컴퓨터:

>>> x.hex()'0x1.921f9f01b866ep+1'

이다 정확한 진수로 표현하는 데 사용될 수 있 reconstructthe float 값을 정확히:

>>> x == float.fromhex('0x1.921f9f01b866ep+1')True

이 표현은 정확한 유용한 안정적으로 포팅 valuesacross 다른 버전의 Python(플랫폼 독립)및 exchangingdata 다른 언어로 지원하는 동일한 형식으로(예:자바고 C99).

또 다른 유용한 도구는 요약 중에 정밀도를 완화시키는 데 도움이되는math.fsum()기능입니다. 실행중인 총계에 값이 추가됨에 따라”손실 된 숫자”를 추적합니다. 을 만들 수 있는 차이에서 전반적인 accuracyso 는 오류를 축적하지 않은 영향을 미치 thefinal 총:

>>> sum( * 10) == 1.0False>>> math.fsum( * 10) == 1.0True

15.1. 표현에 오류가¶

이 섹션에서 설명한”0.1″예에서 세부사항,그리고할 수 있는 방법을 보여 줍니다 performan 정확한 분석이 같은 경우다. Binaryfloating-point 표현에 대한 기본 친숙 함이 가정됩니다.

표현 오류는 일부(대부분,실제로)소수 분수가 이진(기본 2)분수로 정확하게 표현 될 수 없다는 사실을 나타냅니다.이것이 Python(또는 Perl,C,C++,Java,Fortran 및 manyothers)이 종종 기대하는 정확한 십진수를 표시하지 않는 주된 이유입니다.그 이유는 무엇입니까? 1/10 은 이진 분수로 정확하게 표현할 수 없습니다. 거의 모든 기계 오늘(2000 년 11 월)은 IEEE-754 부동 소수점 산술을 사용하며거의 모든 플랫폼은 파이썬 플로트를 IEEE-754″배정밀도”로 매핑합니다. 754doubles 는 53 비트의 정밀도를 포함하므로 입력시 컴퓨터는 0.1 을 j/2**N 형식의 가장 가까운 분수로 변환하여 j 가 정확히 53 비트를 포함하는 정수입니다. 다시 쓰기

1 / 10 ~= J / (2**N)
J ~= 2**N / 10

과 리콜 J 정확히 53 비트(은>= 2**52< 2**53),에 대한 최고의 가치 N56:

>>> 2**52 <= 2**56 // 10 < 2**53True

는,56 은 N 값에는 나뭇잎 J 정확히 53 비트입니다. 그런 다음 j 에 대한 가능한 값은 해당 지수가 반올림됩니다:

>>> q, r = divmod(2**56, 10)>>> r6

이후의 나머지 절반 이상 10 의 최상의 근사 obtainedby 반올림:

>>> q+17205759403792794

따라서 최상의 근사치 1/10 에 754 이중 정밀도:

7205759403792794 / 2 ** 56

나누어 모두의 분자와 분모에 의해 두 개의 감소 분수:

3602879701896397 / 2 ** 55

참고 이후 우리는 둥근,이것은 실제로 조금 더 큰 것보다 1/10;우리가 반올림하지 않았다면,지수는 1/10 보다 조금 작았을 것입니다. 그러나 어떠한 경우에도 정확히 1/10 이 될 수 없습니다!

그리고 컴퓨터지”볼 수”1/10:무엇을 보고 정확한 분수 givenabove,최 754 배 근사치 그것을 얻을 수 있습니다:

>>> 0.1 * 2 ** 553602879701896397.0

경우 우리는 곱는 부분은 10**55,우리가 볼 수있는 가치 to55 소수 자릿수:

>>> 3602879701896397 * 10 ** 55 // 2 ** 551000000000000000055511151231257827021181583404541015625

의미는 정확한 숫자가 컴퓨터에 저장된 동등록 decimal 값은 0 입니다.1000000000000000055511151231257827021181583404541015625.를 표시하는 대신 전체 소수 값,많은 언어(includingolder versions of Python),라운드 결과 17 자리:

>>> format(0.1, '.17f')'0.10000000000000001'

fractionsdecimal모듈을 이 calculationseasy:

>>> from decimal import Decimal>>> from fractions import Fraction>>> Fraction.from_float(0.1)Fraction(3602879701896397, 36028797018963968)>>> (0.1).as_integer_ratio()(3602879701896397, 36028797018963968)>>> Decimal.from_float(0.1)Decimal('0.1000000000000000055511151231257827021181583404541015625')>>> format(Decimal.from_float(0.1), '.17')'0.10000000000000001'

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 항목은 *(으)로 표시합니다