In diesem Artikel wird anhand von Python erläutert, warum Gleitkommaoperationen Fehler verursachen. Bitte erläutern Sie die Umstände, unter denen Fehler auftreten können. und wie kann man es lösen? Hoffe es hilft.
[Verwandte Empfehlung: Python3-Video-Tutorial]
Jeder wird beim Schreiben von Code auf den sogenannten Gleitkommafehler stoßen, ich kann das nur sagen Du hast zu viel Glück.
Nehmen Sie das Python-Bild unten als Beispiel: 0,1 + 0,2
ist nicht gleich 0,3
und 8,7 / 10
ist nicht gleich 0,87 Code>, aber 0,869999…
, es ist so seltsam0.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
Aber das ist definitiv kein Fehler in der Unterwelt, noch ist es ein Problem mit Pythons Design, sondern eine unvermeidliche Folge von Gleitkommazahlen Zahlen beim Ausführen von Operationen, also auch wenn das Gleiche in JavaScript oder anderen Sprachen gilt:
Bevor wir darüber sprechen, warum Gleitkommafehler auftreten, wollen wir zunächst darüber sprechen, wie das geschieht Der Computer verwendet 0 und 1, um eine Ganzzahl darzustellen. Jeder sollte die Binärzahl kennen: Beispielsweise steht 101
für $2^2 + 2^0$, also 5, 1010
stellt $2^3 + 2^ 1$ dar und ist 10.
0000...0000
, also 0, und der Höchstwert ist 1111.. .1111
stellt $2^{31} + 2^{30} + ... + 2^1 + 2^0$ dar, was 4294967295 ist🎜🎜Aus der Perspektive der Permutation und Kombination, weil jedes Bit Es kann 0 oder 1 sein. Der Wert der gesamten Variablen hat $2^{32}$ Möglichkeiten, sodass sie 🎜genau 🎜 jeden Wert zwischen 0 und $2^{23} - 1$ ohne Fehler ausdrücken kann . . 🎜1000.1
zu werden, und schließlich als $1,0001 mal 2^3$ geschrieben, was der wissenschaftlichen Dezimalschreibweise sehr ähnlich ist. 🎜🎜🎜🎜Gleitkommazahlen mit einfacher Genauigkeit🎜🎜🎜🎜In IEEE 754 werden 32-Bit-Gleitkommazahlen in drei Teile aufgeteilt, nämlich Vorzeichen, Exponent und Bruch, was insgesamt 32 Bit ergibt🎜🎜🎜1,0001
der 0001
nach dem Entfernen von 1.
🎜🎜🎜Also wenn 8.5 im 32-Bit-Format ausgedrückt wird, sollten die Wörter wie folgt lauten: 🎜🎜🎜🎜🎜🎜🎜Unter welchen Umständen wird Fehler auftreten? 🎜🎜🎜🎜Das oben erwähnte Beispiel von 8,5 kann als $2^3 + (cfrac{1}{2})^1$ ausgedrückt werden, da 8 und 0,5 Potenzen von 2 sind, sodass es kein Genauigkeitsproblem gibt. 🎜Aber wenn es 8,9 ist, gibt es keine Möglichkeit, Potenzen von 2 zu addieren, daher wird es gezwungen, als $1,0001110011... mal 2^3$ ausgedrückt zu werden, und es wird auch ein Fehler von etwa $0,0000003$ auftreten Wenn Sie neugierig auf das Ergebnis sind, können Sie auf der Website des IEEE-754 Floating Point Converter herumspielen.
Gleitkommazahlen mit doppelter Genauigkeit
Die zuvor erwähnten Gleitkommazahlen mit einfacher Genauigkeit verwenden nur 32 Bit, um den Fehler zu verringern, definiert IEEE 754 auch, wie 64 Bit verwendet werden Um Gleitkommazahlen darzustellen, was 32 Bit entspricht, hat sich der Bruchteil von 23 Bit auf 52 Bit mehr als verdoppelt, sodass die Genauigkeit natürlich erheblich verbessert wird.
Nehmen Sie gerade 8,9 als Beispiel. Obwohl es genauer sein kann, wenn es in 64 Bit ausgedrückt wird, da 8,9 nicht vollständig als Summe von Zweierpotenzen geschrieben werden kann, tritt beim Erreichen von 16 Dezimalstellen immer noch ein Fehler auf Allerdings mit Der Fehler mit einfacher Genauigkeit von 0,0000003 ist im Vergleich viel kleiner
Ähnliche Situationen umfassen 1.0
und 0,999...999
in Python, was sind gleich. 123
und 122.999...999
sind ebenfalls gleich, da die Lücke zwischen ihnen zu klein ist, um in Bruchzahlen dargestellt zu werden, also aus dem Binärformat Sie sind binär. Die Bits sind alle gleich. 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
Lösung
Da der Fehler von Gleitkommazahlen unvermeidbar ist, müssen wir damit leben. Im Folgenden sind zwei gängige Verarbeitungsmethoden aufgeführt:
Stellen Sie den maximal zulässigen Fehler ε (Epsilon) einIn einigen Sprachen wird das sogenannte Epsilon bereitgestellt, damit Sie beurteilen können, ob es innerhalb des zulässigen Bereichs des Gleitkommafehlers liegt. In Python beträgt der Wert von Epsilon ungefähr $2,2e^{-16}$
Damit Sie 0,1 + 0,2 == 0,3 in 0,1 + 0,2 – 0,3 Dies verhindert, dass Gleitkommafehler während der Operation Probleme verursachen, und gewährleistet einen korrekten Vergleich 0,2 gleich 0,3?
Wenn das System es nicht zur Verfügung stellt, können Sie natürlich auch selbst ein Epsilon definieren und es auf etwa 2 hoch -15 einstellen Der Grund für den Gleitkommafehler liegt darin, dass es bei der Konvertierung von Dezimalzahlen in Binärzahlen keine Möglichkeit gibt, alle Dezimalteile in die Mantisse zu stopfen. Da es bei der Konvertierung zu Fehlern kommen kann, führen wir die Berechnungen einfach nicht mit Dezimalzahlen durch direkt. In Python gibt es ein Modul namens „decimal“ und in JavaScript gibt es ein ähnliches Paket. Es kann Ihnen dabei helfen, Berechnungen im Dezimalformat durchzuführen, so wie Sie 0,1 + 0,2 mit Stift und Papier ohne Fehler oder Fehler berechnen können.
🎜🎜🎜🎜Obwohl durch die Verwendung von Dezimalberechnungen Fehler bei Gleitkommazahlen vollständig vermieden werden können, da die Dezimalberechnungen von Decimal simuliert werden, werden Binärberechnungen immer noch in der CPU-Schaltung der untersten Ebene verwendet und die Ausführung ist langsamer als die nativen Gleitkommaberechnungen. Punktoperationen sind viel langsamer, daher wird nicht empfohlen, Decimal für alle Gleitkommaoperationen zu verwenden. 🎜🎜Weitere Kenntnisse zum Thema Programmierung finden Sie unter: 🎜Einführung in die Programmierung🎜! ! 🎜Das obige ist der detaillierte Inhalt vonEine kurze Diskussion darüber, warum Gleitkommaoperationen Fehler erzeugen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!