programing

Python 3에서는 부동소수점 값 4*0.1이 좋아 보이지만 3*0.1은 그렇지 않은 이유는 무엇입니까?

abcjava 2023. 7. 5. 20:01
반응형

Python 3에서는 부동소수점 값 4*0.1이 좋아 보이지만 3*0.1은 그렇지 않은 이유는 무엇입니까?

대부분의 소수점이 정확한 부동소수점 표현을 가지고 있지 않다는 을 알고 있습니다(부동소수점 수학이 깨졌습니까?).

하지만 왜 그런지 모르겠어요4*0.1로 잘 인쇄되어 있습니다.0.4,그렇지만3*0.1그렇지 않습니다. 두 값이 실제로 추한 소수 표현을 가질 때:

>>> 3*0.1
0.30000000000000004
>>> 4*0.1
0.4
>>> from decimal import Decimal
>>> Decimal(3*0.1)
Decimal('0.3000000000000000444089209850062616169452667236328125')
>>> Decimal(4*0.1)
Decimal('0.40000000000000002220446049250313080847263336181640625')

간단한 대답은 다음과 같습니다.3*0.1 != 0.3로 (" " " " " " " " " " " " " " " " " " " " " " " " " " . "4*0.1 == 0.42의 거듭제곱은 일반적으로 "중대한" 연산이기 때문입니다.Python은 표시할 수 있도록 원하는 으로 반올림할 수 있는 가장 짧은 문자열을 찾으려고 합니다.4*0.1~하듯이0.4▁display다없니▁as▁cannot습수를 표시할 수 없습니다.3*0.1~하듯이0.3왜냐하면 이것들은 같지 않기 때문입니다.

당신은 할 수 ..hex파이썬에서 숫자의 내부 표현을 보는 방법(기본적으로 base-10 근사치가 아닌 정확한 이진 부동 소수점 값).이것은 후드 아래에서 무슨 일이 일어나고 있는지 설명하는 데 도움이 될 수 있습니다.

>>> (0.1).hex()
'0x1.999999999999ap-4'
>>> (0.3).hex()
'0x1.3333333333333p-2'
>>> (0.1*3).hex()
'0x1.3333333333334p-2'
>>> (0.4).hex()
'0x1.999999999999ap-2'
>>> (0.1*4).hex()
'0x1.999999999999ap-2'

0.1은 0x1.999999999999a 곱하기 2^-4입니다.끝의 "a"는 숫자 10을 의미합니다. 즉, 이진 부동 소수점의 0.1은 "정확한" 값 0.1보다 약간 큽니다(최종 0x0.99는 0x0.a로 반올림되기 때문입니다).이것에 4를 곱하면 2의 거듭제곱이 되고, 지수는 (2^-4에서 2^-2로) 올라가지만 그 수는 변하지 않습니다.4*0.1 == 0.4.

그러나 3을 곱하면 0x0.99와 0x0.a0(0x0.07) 사이의 작은 차이가 0x0.15 오류로 확대되고 마지막 위치에 한 자릿수 오류로 표시됩니다.이로 인해 0.1*3은 반올림한 값인 0.3보다 약간 큽니다.

의 float 파의 3로트플이썬트▁python.repr라운드 트립이 가능하도록 설계되었습니다. 즉, 표시된 값은 원래 값으로 정확하게 변환할 수 있어야 합니다.float(repr(f)) == f 부동액에 절로대.f를 할 수 따라서 표시할 수 없습니다.0.3그리고.0.1*3정확히 같은 방법으로, 그렇지 않으면 두 의 다른 숫자는 왕복 후에 같은 것이 될 것입니다.따라서 Python 3의repr엔진이 약간의 명백한 오류가 있는 하나를 표시하도록 선택합니다.

repr)strPython 3)에서는 값을 모호하지 않게 만들기 위해 필요한 만큼 숫자를 표시합니다.의 결과입니다.3*0.1에 가장 더 에 00.3(16진수로는 0x1과 하기 위해 더 합니다. 실제로는 LSB가 1개 더 높기 때문에 0.3과 구별하기 위해 더 많은 숫자가 필요합니다.

곱셈은 면에, 곱은셈입니다.4*0.1 는 0.4(16진수로 0x1.999999999ap-2)에 가장 가까운 값을 얻으므로 추가 숫자가 필요하지 않습니다.

이를 쉽게 확인할 수 있습니다.

>>> 3*0.1 == 0.3
False
>>> 4*0.1 == 0.4
True

위의 16진수 표기법을 사용했는데, 이는 두 값 사이의 비트 차이를 보여주기 때문입니다.예를 들어 이 작업을 직접 수행할 수 있습니다. (3*0.1).hex()십진법으로 빛나는 그들을 보고 싶다면, 여기 있습니다.

>>> Decimal(3*0.1)
Decimal('0.3000000000000000444089209850062616169452667236328125')
>>> Decimal(0.3)
Decimal('0.299999999999999988897769753748434595763683319091796875')
>>> Decimal(4*0.1)
Decimal('0.40000000000000002220446049250313080847263336181640625')
>>> Decimal(0.4)
Decimal('0.40000000000000002220446049250313080847263336181640625')

다음은 다른 답변의 간단한 결론입니다.

Python 명령줄에서 float를 확인하거나 인쇄하면 function을 거칩니다.repr문자열 표현을 만듭니다.

3의 3.2 버전, 파썬의이str그리고.repr복잡한 반올림 방식을 사용합니다. 이 방식은 가능하면 보기 좋은 소수점을 선호하지만 플로트와 문자열 표현 간의 객관적(일대일) 매핑을 보장하기 위해 필요한 경우 더 많은 숫자를 사용합니다.

은 이체는다같가보장다니합의 합니다.repr(float(s))수 경우에도 물로정확표예수간없소이보좋다기하는습니서에수점단도한더라게하부유현할예im▁looks▁((좋als,eg다:▁nice▁dec습니보▁simple기▁for▁floats▁if▁they▁can는▁even하부ed▁as서에▁precisely:s = "0.1").

동시에 그것은 그것을 보장합니다.float(repr(x)) == x float에 홀드 모든플에대한 홀드트x

Python의 구현에 특별히 한정되지는 않지만 모든 float to decimal string 함수에 적용되어야 합니다.

부동 소수점 숫자는 본질적으로 이진수이지만, 과학적 표기법에서는 중요한 숫자로 고정된 한계를 가집니다.

기저와 공유되지 않는 소수 인자를 가진 모든 숫자의 역수는 항상 반복적인 점 표현을 초래합니다.예를 들어, 1/7에는 10과 공유되지 않는 소수점 인자 7이 있으므로 반복적인 소수점 표현이 있으며, 2와 공유되지 않는 소수점 인자 5와 1/10에도 마찬가지입니다. 즉, 0.1은 점 다음의 유한 비트 수로 정확하게 표현될 수 없습니다.

0.1에는 정확한 표현이 없기 때문에 근사치를 소수점 문자열로 변환하는 함수는 일반적으로 특정 값을 근사하여 0.10000000004121과 같은 비직관적인 결과를 얻지 않도록 합니다.

부동소수점은 과학적 표기법이므로, 기저의 거듭제곱은 숫자의 지수 부분에만 영향을 미칩니다.예를 들어 10진수 표기의 경우 1.231e+2 * 100 = 1.231e+4이고, 마찬가지로 이진 표기의 경우 1.001010e11 * 100 = 1.001010e101입니다.제가 기저의 비승을 곱하면 유의 자릿수에도 영향을 미칠 것입니다.예: 1.2e1 * 3 = 3.6e1

사용되는 알고리즘에 따라 유의한 수치만을 기반으로 일반적인 소수점을 추측하려고 할 수 있습니다.0.1과 0.4 모두 이진법에서 중요한 수치가 동일한데, 이는 플로트가 기본적으로 (8/5)(2^-4)와 (8/5)(2^-6)의 절단이기 때문입니다.알고리즘이 8/5 sigfig 패턴을 소수점 1.6으로 식별하면 0.1, 0.2, 0.4, 0.8 등에서 작동합니다.또한 플로트 3을 플로트 10으로 나눈 것과 통계적으로 10으로 나눈 것과 같은 다른 조합에 대한 매직 시그프 패턴을 가질 수 있습니다.

3*0.1의 경우, 마지막 몇 개의 중요한 수치는 플로트 3을 플로트 10으로 나누는 것과 다를 수 있으므로 알고리즘이 정밀 손실에 대한 허용 오차에 따라 0.3 상수에 대한 마법 번호를 인식하지 못하게 합니다.

편집: https://docs.python.org/3.1/tutorial/floatingpoint.html

흥미롭게도, 가장 가까운 대략적인 이항 분율을 공유하는 많은 다른 십진수들이 있습니다.예를 들어 숫자 0.1과 0.10000000000000000000055511151231257702118834041015625는 모두 3602879701896397 / 2 **55로 근사됩니다.이러한 모든 십진수 값은 동일한 근사치를 공유하므로 불변 평가(repr(x)) == x를 유지하면서 임의의 값을 표시할 수 있습니다.

정밀도 손실에 대한 공차는 없습니다. 부동 x(0.3)가 부동(0.1*3)과 정확히 동일하지 않으면 repr(x)가 repr(y)와 정확하게 동일하지 않습니다.

언급URL : https://stackoverflow.com/questions/39618943/why-does-the-floating-point-value-of-40-1-look-nice-in-python-3-but-30-1-doesn

반응형