Документация Python неофициальный перевод

floatingpoint.md

196 строк · 14.9 КБ · обычная страница · сырой текст · скачать

1> **Источник:** https://python-all.ru/2.7/tutorial/floatingpoint.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# 14. Арифметика с плавающей точкой: проблемы и ограничения89Числа с плавающей запятой представляются в компьютерном оборудовании в виде двоичных дробей (по основанию 2). Например, десятичная дробь1011```python120.12513```1415имеет значение 1/10 + 2/100 + 5/1000, и точно так же двоичная дробь1617```python180.00119```2021имеет значение 0/2 + 0/4 + 1/8. Эти две дроби имеют одинаковые значения, единственное различие в том, что первая записана в десятичной системе счисления, а вторая – в двоичной.2223К сожалению, большинство десятичных дробей не могут быть точно представлены в виде двоичных дробей. Следствием этого является то, что, вообще говоря, вводимые десятичные числа с плавающей запятой лишь приблизительно соответствуют двоичным числам с плавающей запятой, которые на самом деле хранятся в машине.2425Проблему легче сначала понять в десятичной системе. Рассмотрим дробь 1/3. Её можно приблизить десятичной дробью:2627```python280.329```3031или, лучше,3233```python340.3335```3637или, лучше,3839```python400.33341```4243и так далее. Сколько бы цифр вы ни захотели записать, результат никогда не будет точно равен 1/3, но будет всё лучшим приближением к 1/3.4445Точно так же, сколько бы двоичных цифр вы ни захотели использовать, десятичное значение 0.1 не может быть точно представлено в виде двоичной дроби. В двоичной системе 1/10 – это бесконечно повторяющаяся дробь4647```python480.0001100110011001100110011001100110011001100110011...49```5051Остановка на любом конечном числе битов даёт приближение.5253На типичной машине, работающей под Python, для чисел с плавающей запятой доступно 53 бита точности, поэтому значение, сохраняемое внутри при вводе десятичного числа `0.1`, представляет собой двоичную дробь5455```python560.0001100110011001100110011001100110011001100110011001101057```5859которая близка к 1/10, но не равна ей точно.6061Легко забыть, что сохранённое значение является приближением к исходной десятичной дроби, из-за того, как числа с плавающей запятой отображаются в приглашении интерпретатора. Python печатает лишь десятичное приближение к истинному десятичному значению двоичного приближения, хранящегося в машине. Если бы Python печатал истинное десятичное значение двоичного приближения, сохранённого для 0.1, ему пришлось бы отобразить6263```python64>>> 0.1650.100000000000000005551115123125782702118158340454101562566```6768Это больше цифр, чем обычно нужно, поэтому Python сохраняет число цифр управляемым, отображая вместо этого округлённое значение6970```python71>>> 0.1720.173```7475Важно понимать, что это, в некотором смысле, иллюзия: значение в машине не равно в точности 1/10, вы просто округляете *отображение* истинного машинного значения. Этот факт становится очевидным, как только вы попытаетесь выполнять арифметические операции с этими значениями.7677```python78>>> 0.1 + 0.2790.3000000000000000480```8182Обратите внимание, что это сама суть двоичной арифметики с плавающей запятой: это не ошибка в Python и не ошибка в вашем коде. Вы увидите то же самое во всех языках, поддерживающих аппаратную арифметику с плавающей запятой (хотя некоторые языки могут не *отображать* разницу по умолчанию или во всех режимах вывода).8384Из этого вытекают и другие неожиданности. Например, если попытаться округлить значение 2.675 до двух десятичных знаков, получится следующее8586```python87>>> round(2.675, 2)882.6789```9091В документации к встроенной функции [`round()`](https://python-all.ru/2.7/library/functions.html#round) сказано, что она округляет до ближайшего значения, округляя связи от нуля. Поскольку десятичная дробь 2.675 находится ровно посередине между 2.67 и 2.68, можно было бы ожидать, что результатом будет (двоичное приближение к) 2.68. Однако это не так, потому что при преобразовании десятичной строки `2.675` в двоичное число с плавающей запятой оно снова заменяется двоичным приближением, точное значение которого равно9293```python942.6749999999999998223643160599749535322189331054687595```9697Поскольку это приближение немного ближе к 2.67, чем к 2.68, оно округляется вниз.9899Если вы находитесь в ситуации, когда важно, как округляются десятичные значения, находящиеся ровно посередине, стоит рассмотреть использование модуля [`decimal`](https://python-all.ru/2.7/library/decimal.html#module-decimal). Кстати, модуль [`decimal`](https://python-all.ru/2.7/library/decimal.html#module-decimal) также предоставляет удобный способ «увидеть» точное значение, хранящееся в любом конкретном числе с плавающей запятой Python.100101```python102>>> from decimal import Decimal103>>> Decimal(2.675)104Decimal('2.67499999999999982236431605997495353221893310546875')105```106107Другое следствие: поскольку 0.1 не равно в точности 1/10, суммирование десяти значений 0.1 может не дать точно 1.0:108109```python110>>> sum = 0.0111>>> for i in range(10):112...     sum += 0.1113...114>>> sum1150.9999999999999999116```117118Двоичная арифметика с плавающей запятой таит в себе множество подобных сюрпризов. Проблема с «0.1» подробно объясняется ниже, в разделе «Ошибка представления». См. [Опасности чисел с плавающей запятой](https://python-all.ru/2.7/tutorial/floatingpoint.html) для более полного описания других распространённых сюрпризов.119120Как говорится ближе к концу, «лёгких ответов нет». Тем не менее, не стоит чрезмерно опасаться чисел с плавающей запятой! Ошибки в операциях с float в Python наследуются от аппаратного обеспечения с плавающей запятой, и на большинстве машин составляют не более 1 части на 2\*\*53 за операцию. Этого более чем достаточно для большинства задач, но необходимо помнить, что это не десятичная арифметика, и что каждая операция с float может привести к новой ошибке округления.121122Хотя патологические случаи существуют, при обычном использовании арифметики с плавающей запятой вы в итоге увидите ожидаемый результат, если просто округлите отображение конечных результатов до ожидаемого числа десятичных знаков. Для точного управления отображением чисел с плавающей запятой обратитесь к спецификаторам формата метода [`str.format()`](https://python-all.ru/2.7/library/stdtypes.html#str.format) в [Синтаксисе строк форматирования](https://python-all.ru/2.7/library/string.html#formatstrings).123124## 14.1. Ошибка представления125126В этом разделе подробно объясняется пример с «0.1» и показывается, как можно самостоятельно выполнить точный анализ подобных случаев. Предполагается базовое знакомство с двоичным представлением чисел с плавающей запятой.127128*Ошибка представления* относится к тому факту, что некоторые (на самом деле большинство) десятичных дробей не могут быть точно представлены в виде двоичных (по основанию 2) дробей. Это главная причина, по которой Python (или Perl, C, C++, Java, Fortran и многие другие) часто не отображают точное десятичное число, которое вы ожидаете:129130```python131>>> 0.1 + 0.21320.30000000000000004133```134135Почему так? 1/10 и 2/10 не могут быть точно представлены в виде двоичной дроби. Почти все современные машины (июль 2010) используют арифметику с плавающей запятой IEEE-754, и почти все платформы отображают числа с плавающей запятой Python на «двойную точность» IEEE-754. Числа двойной точности содержат 53 бита точности, поэтому при вводе компьютер стремится преобразовать 0.1 в ближайшую дробь вида *J*/2\*\**N*, где *J* – целое число, содержащее ровно 53 бита. Переписывая136137```python1381 / 10 ~= J / (2**N)139```140141как142143```python144J ~= 2**N / 10145```146147и вспоминая, что *J* имеет ровно 53 бита (`>= 2**52`, но `< 2**53`), наилучшим значением для *N* является 56:148149```python150>>> 2**521514503599627370496152>>> 2**531539007199254740992154>>> 2**56/101557205759403792793156```157158То есть 56 – единственное значение для *N*, при котором *J* содержит ровно 53 бита. Наилучшее возможное значение для *J* тогда – это округлённое частное:159160```python161>>> q, r = divmod(2**56, 10)162>>> r1636164```165166Поскольку остаток больше половины 10, наилучшее приближение получается округлением вверх:167168```python169>>> q+11707205759403792794171```172173Следовательно, наилучшее возможное приближение к 1/10 в двойной точности 754 – это число, превышающее 2\*\*56, или174175```python1767205759403792794 / 72057594037927936177```178179Обратите внимание, что поскольку мы округлили вверх, это значение на самом деле немного больше 1/10; если бы мы не округлили вверх, частное было бы немного меньше 1/10. Но ни в коем случае оно не может быть *точно* равным 1/10!180181Таким образом, компьютер никогда не «видит» 1/10: он видит точную дробь, приведённую выше, – наилучшее приближение двойной точности 754, которое он может получить:182183```python184>>> .1 * 2**561857205759403792794.0186```187188Если умножить эту дробь на 10\*\*30, можно увидеть (усечённое) значение её 30 наиболее значащих десятичных цифр:189190```python191>>> 7205759403792794 * 10**30 // 2**56192100000000000000005551115123125L193```194195что означает, что точное число, хранящееся в компьютере, приблизительно равно десятичному значению 0.100000000000000005551115123125. В версиях до Python 2.7 и Python 3.1 Python округлял это значение до 17 значащих цифр, получая '0.10000000000000001'. В текущих версиях Python отображает значение, основанное на кратчайшей десятичной дроби, которая правильно округляется обратно к истинному двоичному значению, что даёт просто '0.1'.196