floatingpoint.md
1> **Источник:** https://python-all.ru/3.0/tutorial/floatingpoint.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# Арифметика с плавающей запятой: проблемы и ограничения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```python54>>> 0.1550.1000000000000000156```5758На большинстве современных машин это именно то, что вы увидите, если введёте 0.1 в приглашении Python. Однако может быть иначе, поскольку количество битов, используемых оборудованием для хранения значений с плавающей запятой, может различаться на разных машинах, и Python выводит только десятичное приближение к истинному десятичному значению двоичного приближения, хранящегося на машине. На большинстве машин, если бы Python выводил истинное десятичное значение двоичного приближения, хранящегося для 0.1, ему пришлось бы отобразить5960```python61>>> 0.1620.100000000000000005551115123125782702118158340454101562563```6465Напротив! Приглашение Python использует встроенную функцию [`repr()`](https://python-all.ru/3.0/library/functions.html#repr) для получения строкового представления всего, что оно отображает. Для чисел с плавающей запятой `repr(float)` округляет истинное десятичное значение до 17 значащих цифр, выдавая6667```python680.1000000000000000169```7071`repr(float)` выдаёт 17 значащих цифр, поскольку оказывается, что этого достаточно (на большинстве машин), чтобы `eval(repr(x)) == x` для всех конечных чисел с плавающей запятой *x*, но округление до 16 цифр уже не обеспечивает этого.7273Обратите внимание, что это в самой природе двоичной плавающей арифметики: это не ошибка в Python и не ошибка в вашем коде. То же самое вы увидите во всех языках, которые поддерживают арифметику с плавающей точкой вашего оборудования (хотя некоторые языки могут не *показывать* различие по умолчанию или во всех режимах вывода).7475Встроенная функция [`str()`](https://python-all.ru/3.0/library/functions.html#str) в Python выдаёт только 12 значащих цифр, и, возможно, вы предпочтёте использовать её. Обычно `eval(str(x))` не воспроизводит *x*, но вывод может быть более приятным для глаз:7677```python78>>> print(str(0.1))790.180```8182Важно понимать, что в определённом смысле это иллюзия: значение в машине не равно в точности 1/10, вы просто округляете *отображение* истинного машинного значения.8384Из этого вытекают и другие неожиданности. Например, после того как увидите8586```python87>>> 0.1880.1000000000000000189```9091вам может захотеться использовать функцию [`round()`](https://python-all.ru/3.0/library/functions.html#round), чтобы сократить его до ожидаемой одной цифры. Но это ничего не меняет:9293```python94>>> round(0.1, 1)950.1000000000000000196```9798Проблема в том, что двоичное значение с плавающей запятой, хранящееся для «0.1», уже было наилучшим возможным двоичным приближением к 1/10, поэтому попытка округлить его снова не может улучшить: оно уже настолько хорошо, насколько это возможно.99100Другое следствие: поскольку 0.1 не равно точно 1/10, сумма десяти значений 0.1 может не дать в точности 1.0:101102```python103>>> sum = 0.0104>>> for i in range(10):105... sum += 0.1106...107>>> sum1080.99999999999999989109```110111Двоичная арифметика с плавающей запятой таит в себе множество подобных сюрпризов. Проблема с «0.1» подробно объясняется ниже, в разделе «Ошибка представления». См. [Опасности чисел с плавающей запятой](https://python-all.ru/3.0/tutorial/floatingpoint.html) для более полного описания других распространённых сюрпризов.112113Как говорится ближе к концу, «лёгких ответов нет». Тем не менее, не стоит чрезмерно опасаться чисел с плавающей запятой! Ошибки в операциях с float в Python наследуются от аппаратного обеспечения с плавающей запятой, и на большинстве машин составляют не более 1 части на 2\*\*53 за операцию. Этого более чем достаточно для большинства задач, но необходимо помнить, что это не десятичная арифметика, и что каждая операция с float может привести к новой ошибке округления.114115Хотя патологические случаи существуют, при обычном использовании арифметики с плавающей запятой в конечном итоге вы увидите ожидаемый результат, если просто округлить отображение окончательных результатов до нужного количества десятичных знаков. [`str()`](https://python-all.ru/3.0/library/functions.html#str) обычно достаточно, а для более точного управления смотрите [`str.format()`](https://python-all.ru/3.0/library/stdtypes.html#str.format) спецификаторы формата метода в [*Синтаксис строк форматирования*](https://python-all.ru/3.0/library/string.html#formatstrings).116117Для случаев, требующих точного десятичного представления, попробуйте использовать модуль [`decimal`](https://python-all.ru/3.0/library/decimal.html#module-decimal), который реализует десятичную арифметику, подходящую для бухгалтерских приложений и приложений с высокой точностью.118119Другой вид точной арифметики поддерживается модулем [`fractions`](https://python-all.ru/3.0/library/fractions.html#module-fractions), который реализует арифметику на основе рациональных чисел (так что числа вроде 1/3 могут быть представлены точно).120121Если вы активно используете операции с плавающей запятой, вам стоит взглянуть на пакет Numerical Python и многие другие пакеты для математических и статистических операций, предоставляемые проектом SciPy. См. \<[http://scipy.org](https://python-all.ru/3.0/tutorial/floatingpoint.html)\>.122123Python предоставляет инструменты, которые могут помочь в тех редких случаях, когда вы действительно *хотите* узнать точное значение числа с плавающей запятой. Метод [`float.as_integer_ratio()`](https://python-all.ru/3.0/library/stdtypes.html#float.as_integer_ratio) выражает значение числа с плавающей запятой в виде дроби:124125```python126>>> x = 3.14159127>>> x.as_integer_ratio()128(3537115888337719L, 1125899906842624L)129```130131Поскольку отношение точное, его можно использовать для восстановления исходного значения без потерь:132133```python134>>> x == 3537115888337719 / 1125899906842624135True136```137138Метод [`float.hex()`](https://python-all.ru/3.0/library/stdtypes.html#float.hex) выражает число с плавающей запятой в шестнадцатеричном формате (основание 16), снова предоставляя точное значение, хранящееся на вашем компьютере:139140```python141>>> x.hex()142'0x1.921f9f01b866ep+1'143```144145Это точное шестнадцатеричное представление можно использовать для точного восстановления значения числа с плавающей запятой:146147```python148>>> x == float.fromhex('0x1.921f9f01b866ep+1')149True150```151152Поскольку представление точное, оно полезно для надёжного переноса значений между разными версиями Python (независимость от платформы) и обмена данными с другими языками, поддерживающими тот же формат (например, Java и C99).153154## Ошибка представления155156В этом разделе подробно объясняется пример «0.1» и показывается, как можно самостоятельно выполнить точный анализ подобных случаев. Предполагается базовое знакомство с двоичным представлением чисел с плавающей запятой.157158*Ошибка представления* относится к тому факту, что некоторые (на самом деле большинство) десятичных дробей не могут быть точно представлены в виде двоичных (по основанию 2) дробей. Это главная причина, по которой Python (или Perl, C, C++, Java, Fortran и многие другие) часто не отображают точное десятичное число, которое вы ожидаете:159160```python161>>> 0.11620.10000000000000001163```164165Почему так? 1/10 не может быть точно представлено в виде двоичной дроби. Почти все современные машины (ноябрь 2000 года) используют арифметику с плавающей запятой IEEE-754, и почти все платформы отображают числа с плавающей запятой Python на «двойную точность» IEEE-754. Числа двойной точности 754 содержат 53 бита точности, поэтому при вводе компьютер стремится преобразовать 0.1 в ближайшую возможную дробь вида *J*/2\*\**N*, где *J* – целое число, содержащее ровно 53 бита. Переписывая166167```python1681 / 10 ~= J / (2**N)169```170171как172173```python174J ~= 2**N / 10175```176177и вспоминая, что *J* имеет ровно 53 бита (`>= 2**52`, но `< 2**53`), наилучшим значением для *N* является 56:178179```python180>>> 2**521814503599627370496182>>> 2**531839007199254740992184>>> 2**56/101857205759403792794.0186```187188То есть 56 – единственное значение для *N*, которое оставляет *J* с ровно 53 битами. Наилучшее возможное значение для *J* тогда – это округлённое частное:189190```python191>>> q, r = divmod(2**56, 10)192>>> r1936194```195196Поскольку остаток больше половины 10, наилучшее приближение получается округлением вверх:197198```python199>>> q+12007205759403792794201```202203Следовательно, наилучшее возможное приближение к 1/10 в двойной точности 754 – это число, превышающее 2\*\*56, или204205```python2067205759403792794 / 72057594037927936207```208209Обратите внимание, что, поскольку мы округлили вверх, это значение на самом деле немного больше 1/10; если бы мы не округлили вверх, частное было бы немного меньше 1/10. Но в любом случае оно не может быть *точно* 1/10!210211Таким образом, компьютер никогда не «видит» 1/10: он видит точную дробь, приведённую выше, – наилучшее приближение двойной точности 754, которое он может получить:212213```python214>>> .1 * 2**562157205759403792794.0216```217218Если умножить эту дробь на 10\*\*30, можно увидеть (усечённое) значение её 30 наиболее значащих десятичных цифр:219220```python221>>> 7205759403792794 * 10**30 / 2**56222100000000000000005551115123125223```224225то есть точное число, хранящееся в компьютере, приблизительно равно десятичному значению 0.100000000000000005551115123125. Округление этого до 17 значащих цифр даёт 0.10000000000000001, которое Python отображает (точнее, будет отображать на любой платформе, соответствующей 754 и выполняющей наилучшие возможные преобразования ввода и вывода в своей библиотеке C – ваша может и не!).226