>  기사  >  백엔드 개발  >  부동 소수점 연산에서 오류가 발생하는 이유에 대한 간략한 설명

부동 소수점 연산에서 오류가 발생하는 이유에 대한 간략한 설명

青灯夜游
青灯夜游앞으로
2021-07-15 18:53:0210390검색

이 기사에서는 Python을 예로 들어 부동 소수점 연산에서 오류가 발생하는 이유에 대해 설명합니다. 오류가 발생할 수 있는 상황을 소개해주세요. 어떻게 해결하나요? 도움이 되길 바랍니다.

부동 소수점 연산에서 오류가 발생하는 이유에 대한 간략한 설명

[관련 추천: Python3 동영상 튜토리얼 ]

누구나 코드를 작성할 때 소위 부동 소수점 오류에 직면하게 됩니다. 당신은 정말 운이 좋다.

아래 Python 그림을 예로 들면, 0.1 + 0.20.3과 같지 않고, 8.7 / 10은 0.87 code>인데 0.869999…, 너무 이상해요0.1 + 0.2 并不等于 0.38.7 / 10 也不等于 0.87,而是 0.869999…,真是太奇怪了

부동 소수점 연산에서 오류가 발생하는 이유에 대한 간략한 설명

但这绝对不是什么阴间 bug,也不是 Python 设计得有问题,而是浮点数在做运算时必然的结果,所以即便是在 JavaScript 或其他语言中也都是一样:

부동 소수점 연산에서 오류가 발생하는 이유에 대한 간략한 설명

电脑是怎样储存一个整数的(Integer)

在讲为什么会存在浮点误差之前,先来谈谈电脑是怎么用 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$ 之间的任一个值,不会有任何误差。

浮点数(Floating Point)

虽然从 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

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

부동 소수점 연산에서 오류가 발생하는 이유에 대한 간략한 설명

  • 数符(sign):最左侧的 1 bit 代表正负号,正数的话 sign 就为 0,反之则是 1
  • 阶码(exponent):中间的 8 bit 代表规格化之后的次方数,采用的是 阶码真值 +127 的格式,也就是 3 还要再加上 127 等于 130
  • 尾数(fraction):最右侧的 23 bit 放的是小数部分,以 1.0001 来说就是去掉 1. 之后的 0001
  • 부동 소수점 연산에서 오류가 발생하는 이유에 대한 간략한 설명

하지만 이건 절대 저승의 버그도 아니고 파이썬 설계의 문제도 아니고 부동 소수점의 불가피한 결과입니다 따라서 JavaScript나 다른 언어에서도 마찬가지입니다.

부동 소수점 연산에서 오류가 발생하는 이유에 대한 간략한 설명

컴퓨터가 정수(정수)를 저장하는 방법

부동 소수점 오류가 발생하는 이유에 대해 이야기하기 전에 먼저 컴퓨터는 0과 1을 사용하여 정수를 나타냅니다. 모두가 이진수를 알아야 합니다. 예를 들어 101는 $2^2 + 2^0$, 즉 5, 1010을 나타냅니다. $2^3 + 2^ 1을 의미합니다. $는 10입니다.

부동 소수점 연산에서 오류가 발생하는 이유에 대한 간략한 설명

If 부호 없는 32비트 정수로 0이나 1이 들어갈 수 있는 자리가 32개 있다는 뜻이므로 최소값은 0000...0000 즉 0이고, 최대값은 1111.. .1111은 $2^{31} + 2^{30} + ... + 2^1 + 2^0$을 나타내며 이는 순열의 관점에서 4294967295🎜🎜입니다. 조합, 왜냐하면 각 비트는 0 또는 1일 수 있기 때문입니다. 전체 변수의 값은 $2^{32}$ 가능성을 가지므로 0에서 $2^{23} - 1$ 사이의 모든 값을 오류 없이 🎜정확하게 🎜 표현할 수 있습니다. . 🎜

🎜부동 소수점🎜

🎜0과 $2^{23} - 1$ 사이에는 많은 정수가 있지만 그 수는 결국 🎜제한되어 있으므로 $2^{ 32}$는 그만큼 많습니다. ; 그러나 부동 소수점 숫자는 이와 다릅니다. 1부터 10까지의 범위에는 10개의 정수만 있지만 5.1, 5.11, 5.111 등과 같이 무한히 많은 부동 소수점 숫자가 있습니다. 모두 나열할 수는 없습니다. 🎜🎜하지만 32비트 공간에는 가능성이 2³²밖에 없기 때문에 모든 부동 소수점 숫자를 이 32비트 공간에 넣기 위해 많은 CPU 제조업체에서는 다양한 부동 소수점 숫자 표현 방법을 발명했지만, 모든 CPU가 형식이 모두 다르고 번거롭기 때문에 결국 IEEE에서 발표한 IEEE 754가 일반적인 부동 소수점 계산 표준으로 사용되었습니다. 오늘날의 CPU도 이 표준에 따라 설계되었습니다. 🎜

🎜IEEE 754🎜

🎜IEEE 754는 단정밀도(32비트), 배정밀도(64비트) 및 특수 값(무한대, NaN) 등의 표현을 포함하여 많은 것을 정의합니다. 🎜 🎜🎜🎜Normalization🎜🎜🎜🎜 부동 소수점 숫자 8.5를 예로 들어 IEEE 754 형식으로 변경하려면 먼저 몇 가지 정규화 처리를 수행해야 합니다. 즉, 8.5를 8 + 0.5로 분할하면 $2^3 +입니다. (cfrac{1 }{2})^1$, 이진수로 작성하면 1000.1가 되고, 마지막으로 $1.0001 곱하기 2^3$로 작성됩니다. 이는 십진 과학 표기법과 매우 유사합니다. 🎜🎜🎜🎜단정밀도 부동 소수점 숫자🎜🎜🎜🎜IEEE 754에서 32비트 부동 소수점 숫자는 부호, 지수, 분수의 세 부분으로 나누어져 총합이 32비트입니다🎜🎜🎜
  • 부호: 가장 왼쪽 1비트는 양수 및 음수 부호를 나타냅니다. 양수이면 부호는 0이고, 그렇지 않으면 1입니다.
  • 지수: 가운데 8비트는 🎜코드 형식을 사용하여 정규화 후의 거듭제곱을 나타냅니다. 참값 + 127🎜, 즉 3 더하기 127은 130🎜
  • 가수(분수): 맨 오른쪽의 23비트 입력되는 것은 소수 부분입니다. 예를 들어 1.00011.을 제거한 후 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.00.999...999가 포함됩니다. 123122.999...999도 동일합니다. 그 이유는 둘 사이의 간격이 너무 작아서 분수로 표시할 수 없기 때문입니다. 따라서 이진 형식에서는 각각 그것들은 바이너리입니다. 비트는 모두 동일합니다. 1.00.999...999 是相等的、123122.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과 같나요?

    물론 시스템에서 제공하지 않는 경우 엡실론을 직접 정의하여 2의 -15승 정도로 설정할 수도 있습니다.

    🎜완전히 십진법을 사용합니다🎜🎜🎜🎜 부동 소수점 오류는 10진수를 2진수로 변환하는 과정에서 모든 소수 부분을 가수에 넣을 수 있는 방법이 없기 때문에 변환 시 오류가 발생할 수 있으므로 단순히 10진수를 변환하여 계산을 수행하지 않습니다. 곧장. 🎜🎜파이썬에는 십진수라는 모듈이 있고, 자바스크립트에도 비슷한 패키지가 있습니다. 펜과 종이를 사용하여 오류나 오류 없이 0.1 + 0.2를 계산할 수 있는 것처럼 소수점 이하 계산을 수행하는 데 도움이 될 수 있습니다. 🎜🎜🎜🎜🎜십진수 계산을 사용하면 부동 소수점 수의 오류를 완전히 피할 수 있지만 Decimal의 십진수 계산은 시뮬레이션되기 때문에 이진수 계산은 여전히 ​​가장 낮은 수준의 CPU 회로에서 사용되며 실행은 기본 부동 소수점 계산보다 느립니다. 소수점 연산은 훨씬 느리므로 모든 부동 소수점 연산에 Decimal을 사용하지 않는 것이 좋습니다. 🎜🎜더 많은 프로그래밍 관련 지식을 보려면 🎜프로그래밍 소개🎜를 방문하세요! ! 🎜

위 내용은 부동 소수점 연산에서 오류가 발생하는 이유에 대한 간략한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 segmentfault.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제