이 기사에서는 Python을 예로 들어 부동 소수점 연산에서 오류가 발생하는 이유에 대해 설명합니다. 오류가 발생할 수 있는 상황을 소개해주세요. 어떻게 해결하나요? 도움이 되길 바랍니다.
[관련 추천:Python3 동영상 튜토리얼]
누구나 코드를 작성할 때 소위 부동 소수점 오류에 직면하게 됩니다. 당신은 정말 운이 좋다.
아래 Python 그림을 예로 들면,0.1 + 0.2
는0.3
과 같지 않고,8.7 / 10
은 0.87 code>인데0.869999…
, 너무 이상해요0.1 + 0.2
并不等于0.3
,8.7 / 10
也不等于0.87
,而是0.869999…
,真是太奇怪了
但这绝对不是什么阴间 bug,也不是 Python 设计得有问题,而是浮点数在做运算时必然的结果,所以即便是在 JavaScript 或其他语言中也都是一样:
在讲为什么会存在浮点误差之前,先来谈谈电脑是怎么用 0 跟 1 来表示一个整数的,大家应该都知道二进制:例如101
代表 ^2 + 2^0$ 也就是 5、1010
代表 ^3 + 2^1$ 也就是 10。
如果是一个无符号的 32 bit 整数,代表它有 32 个位置可以放 0 或 1,所以最小值就是0000...0000
也就是 0,而最大值1111...1111
代表 ^{31} + 2^{30} + ... + 2^1 + 2^0$ 也就是 4294967295
从排列组合的角度来看,因为每一个 bit 位都可以是 0 或 1,整个变量的值有 ^{32}$ 种可能,所以可以精确的表达出 0 到 ^{23} - 1$ 之间的任一个值,不会有任何误差。
虽然从 0 到 ^{23} - 1$ 之间存在很多个整数,但其数量终究是有限的,就是 ^{32}$ 那么多个而已;但浮点数就不同了,我们可以这样想:在 1 到 10 这个区间中只有十个整数,却有无穷多个浮点数,例如 5.1、5.11、5.111 等等,怎么也列举不完。
但因为在 32 bit 的空间中就只有 2³² 种可能性,为了把所有浮点数都塞在这个 32 bit 的空间里面,许多 CPU 厂商发明了各种浮点数的表示方式,但如果每家 CPU 的格式都不一样也很麻烦,所以最后是以 IEEE 发布的 IEEE 754 作为通用的浮点数运算标准,现在的 CPU 也都遵循这个标准进行设计。
IEEE 754 里面定义了很多东西,其中包括单精度(32 bit)、双精度(64 bit)和特殊值(无穷大、NaN)的表示方式等等
规格化
以 8.5 这个浮点数来说,如果要变成 IEEE 754 格式的话必须先做一些规格化处理:把 8.5 拆成 8 + 0.5 也就是 ^3 + (cfrac{1}{2})^1$ ,接着写成二进位变成1000.1
,最后再写成 .0001 times 2^3$,与十进制的科学记数法很相似。
单精度浮点数
在 IEEE 754 中 32 bit 浮点数被拆分成三个部分,分别是 数符(sign)、阶码(exponent) 和尾数(fraction),加起来总共是 32 个 bit
1.0001
来说就是去掉1.
之后的0001
하지만 이건 절대 저승의 버그도 아니고 파이썬 설계의 문제도 아니고 부동 소수점의 불가피한 결과입니다 따라서 JavaScript나 다른 언어에서도 마찬가지입니다.
부동 소수점 오류가 발생하는 이유에 대해 이야기하기 전에 먼저 컴퓨터는 0과 1을 사용하여정수를 나타냅니다. 모두가 이진수를 알아야 합니다. 예를 들어101
는 $2^2 + 2^0$, 즉 5,1010
을 나타냅니다. $2^3 + 2^ 1을 의미합니다. $는 10입니다.
0000...0000
즉 0이고, 최대값은
1111.. .1111
은 $2^{31} + 2^{30} + ... + 2^1 + 2^0$을 나타내며 이는 순열의 관점에서 4294967295입니다. 조합, 왜냐하면 각 비트는 0 또는 1일 수 있기 때문입니다. 전체 변수의 값은 $2^{32}$ 가능성을 가지므로 0에서 $2^{23} - 1$ 사이의 모든 값을 오류 없이 정확하게 표현할 수 있습니다. .
1000.1
가 되고, 마지막으로 $1.0001 곱하기 2^3$로 작성됩니다. 이는 십진 과학 표기법과 매우 유사합니다. 단정밀도 부동 소수점 숫자IEEE 754에서 32비트 부동 소수점 숫자는 부호, 지수, 분수의 세 부분으로 나누어져 총합이 32비트입니다
1.0001
은1.
을 제거한 후0001
그래서 8.5가 32비트 형식으로 표현된다면 단어는 다음과 같아야 합니다: 어떤 상황에서 오류가 발생합니까? 위에 언급된 8.5의 예는 $2^3 + (cfrac{1}{2})^1$로 표현될 수 있습니다. 8과 0.5가 우연히 2의 거듭제곱이므로 정확도 문제가 발생하지 않기 때문입니다.하지만 8.9라면 2의 거듭제곱을 더할 방법이 없기 때문에 어쩔 수 없이 $1.0001110011... 곱하기 2^3$로 표현되어야 하고, 역시 $0.0000003$ 정도의 오차가 발생합니다. 결과가 궁금하신 경우 IEEE-754 부동 소수점 변환기 웹사이트로 이동하여 시험해 보실 수 있습니다.
이배정밀도 부동 소수점 숫자
앞서 언급한 단정밀도 부동 소수점 숫자는 오류를 줄이기 위해 32비트만 사용하여 표현합니다. IEEE 754에서는 64비트를 사용하는 방법도 정의합니다. 32비트와 동일한 부동 소수점 수를 표현하기 위해 비트에 비해 분수 부분이 23비트에서 52비트로 두 배 이상 늘어났기 때문에 당연히 정확도가 많이 향상될 것입니다.
지금 8.9를 예로 들어보겠습니다. 64비트로 표현하면 더 정확할 수 있지만 8.9는 2의 거듭제곱의 합으로 완전히 쓸 수 없기 때문에 소수점 16자리에 도달하면 여전히 오류가 발생합니다. 그러나 0.0000003의 단정밀도 오류는 비교하면 훨씬 작습니다
유사한 상황에는 Python의1.0
및0.999...999
가 포함됩니다.123
및122.999...999
도 동일합니다. 그 이유는 둘 사이의 간격이 너무 작아서 분수로 표시할 수 없기 때문입니다. 따라서 이진 형식에서는 각각 그것들은 바이너리입니다. 비트는 모두 동일합니다.1.0
跟0.999...999
是相等的、123
跟122.999...999
也是相等的,因为他们之间的差距已经小到无法放在 fraction 里面,所以从二进制格式看来它们每一个二进制位都是一样的。
既然浮点数的误差是无法避免的,那就只好跟它共处了,下面是两个比较常见的处理方法:
设定最大允许误差 ε (epsilon)
在某些语言会提供所谓的 epsilon,用来让你判断是不是在浮点误差的允许范围内,以 Python 来说 epsilon 的值大约是 .2e^{-16}$
所以你可以把0.1 + 0.2 == 0.3
改写成0.1 + 0.2 — 0.3
Solution
부동소수점 숫자의 오류는 피할 수 없으므로 이를 감수해야 합니다. 다음은 두 가지 일반적인 처리 방법입니다.
최대 허용 오류 ε(엡실론)을 설정합니다.일부 언어에서는 부동 소수점 오류 허용 범위 내에 있는지 판단할 수 있도록 소위 엡실론이 제공됩니다. Python에서는 엡실론의 값이 약 $2.2e^{-16}$다시 작성할 수 있도록 0.1 + 0.2 == 0.3을0.1 + 0.2 — 0.3 으로 만듭니다. 이렇게 하면 작업 중에 부동 소수점 오류로 인해 문제가 발생하는 것을 방지하고 0.1 plus를 올바르게 수행할 수 있습니다. 0.2는 0.3과 같나요?
위 내용은 부동 소수점 연산에서 오류가 발생하는 이유에 대한 간략한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!