Об ошибке округления python и числах с плавающей точкой - PullRequest
0 голосов
/ 22 апреля 2020
>>> 1/3
0.3333333333333333

>>> 1/3+1/3+1/3
1.0 

Я не могу понять, почему это 1.0. Разве это не должно быть 0.9999999999999999? Так что я как бы придумала решение, которое python имеет автоматическое округление c для его ответа, но если чем, то следующие результаты не могут быть объяснены ...

>>> 1/3+1/3+1/3+1/3+1/3+1/3
1.9999999999999998

>>> (1/3+1/3+1/3)+(1/3+1/3+1/3)
2.0

Я думал произошла ошибка округления, потому что в мантиссе было только ограниченное количество цифр, и показатель степени (в числах с плавающей запятой), но 0.9999 ~~ 9 тоже не является пределом количества цифр. Может кто-нибудь объяснить, почему эти результаты вышли вот так?

Ответы [ 3 ]

2 голосов
/ 22 апреля 2020

В большинстве реализаций Python используется двоичный формат с плавающей запятой, чаще всего формат IEEE-754 binary64. Этот формат имеет нет десятичных цифр. Он имеет 53 двоичных разряда.

Когда этот формат используется с округлением до ближайшего отношения к четному, вычисление 1/3 дает 0,333333333333333314829616256247390992939472198486328125. Ваша реализация Python не показывает полное значение по умолчанию; он показывает «0,3333333333333333», что вводит вас в заблуждение.

Когда это число добавляется к себе, результат составляет 0,66666666666666662965923251249478198587894439697265625. Этот результат точен; нет ошибки округления. (То есть, у него нет новой ошибки округления; это в точности сумма 0,333333333333333314829616256247390992939472198486328125 с самим собой.)

Когда 0,3333333333333333148296162562473909929394721984863225212498486328, добавленное число 53 не добавляется, реальное число не возвращается, реальное число не добавляется. Таким образом, результат должен быть округлен. Это округление происходит с округлением в большую сторону, и получается ровно 1.

Когда 0,333333333333333314829616256247390992939472198486328125 снова добавляется, результат снова не соответствует и округляется. На этот раз, округление случается вниз и производит 1.3333333333333332593184650249895639717578887939453125.

Последующие дополнения производят 1.666666666666666518636930049979127943515777587890625, а затем 1.9999999999999997779553950749686919152736663818359375, которые ваша Python отображает реализацию, как «1.9999999999999998».

Когда вы группировать арифмети c как (1/3+1/3+1/3) + (1/3+1/3+1/3), затем 1 получается для каждого элемента в скобках, как объяснено выше, и 1 + 1, конечно, 2.

1 голос
/ 22 апреля 2020

Это одна из тонких точек арифметики IEEE-754 c. Когда вы пишете:

>>> 1/3
0.3333333333333333

напечатанное число является «округленной» версией числа, которое внутренне сохраняется как результат 1/3. Это именно то, что было решено показать конвертацией Double -> String в процессе печати. Но вы уже знали это.

Теперь вы можете спросить, есть ли способ узнать, в чем разница? Да, используйте модуль fractions:

>>> from fractions import Fraction
>>> Fraction(1, 3) - Fraction(1/3)
Fraction(1, 54043195528445952)

Ах, это интересно. Таким образом, оно немного меньше фактического значения, а разница составляет 1 / 54043195528445952. Это, конечно, ожидаемо.

Итак, что происходит, когда вы «складываете» два из них вместе. Давайте посмотрим:

>>> Fraction(2,3) - Fraction(1/3+1/3)
Fraction(1, 27021597764222976)

Опять же, вы близки к 2/3 rds, но все еще не совсем там. Давайте сделаем сложение еще раз:

>>> Fraction(1,1) - Fraction(1/3+1/3+1/3)
Fraction(0, 1)

Бин go! с 3 из них представление точно 1.

Почему это? Ну, в каждом добавлении вы получаете число, близкое к тому, что, по вашему мнению, должно быть ответом, но внутреннее округление приводит к тому, что результат становится близким числом, которое вы не имели в виду. С тремя дополнениями, что говорит вам ваша интуиция и что совпадает внутреннее округление.

Важно подчеркнуть, что сложение 1/3 + 1/3 + 1/3 не делает , а не дает 1; он просто создает внутреннее значение, ближайшее представление которого в виде значения с плавающей точкой двойной точности IEEE-754 равно 1. Это тонкое, но важное отличие. Надеюсь, это поможет!

0 голосов
/ 22 апреля 2020

Этот вопрос может дать некоторые ответы на ошибку с плавающей запятой Не нарушена ли математика с плавающей запятой?

С помощью скобок компилятор разбивает сложение на более мелкие части, уменьшая вероятность с плавающей запятой, которая не должна быть там, чтобы продолжать и «накапливать» ошибку с плавающей запятой. Скорее всего, при разбиении на группы по 3 компилятор знает, что сумма будет равна 1, и сложим 1 + 1 вместе

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...