Я полагаю, что это классический вопрос о точности с плавающей запятой, но я пытаюсь обдумать этот результат, работая 1//0.01
в Python 3.7.5 99
.
Я предполагаю, что это ожидаемый результат, но есть ли способ решить, когда его безопаснее использовать int(1/f)
, чем 1//f
?
round()
и никогда//
илиint()
. Связанный вопрос о сравнении с плавающей запятой не имеет ничего общего с усечением и не так легко исправить.Ответы:
Если бы это было деление с действительными числами,
1//0.01
было бы ровно 100. Поскольку они являются аппроксимациями с плавающей запятой, тем не менее, они0.01
немного больше, чем 1/100, что означает, что частное немного меньше, чем 100. Именно это значение 99. что-то затем вычисляется до 99.источник
Причины такого исхода такие же, как вы утверждаете, и объяснены в « Математика с плавающей запятой нарушена? и многие другие подобные вопросы и ответы.
Когда вы знаете количество десятичных чисел числителя и знаменателя, более надежный способ - сначала умножить эти числа, чтобы они могли рассматриваться как целые числа, а затем выполнить целочисленное деление для них:
Так что в вашем случае
1//0.01
следует сначала преобразовать в1*100//(0.01*100)
100.В более экстремальных случаях вы все равно можете получить «неожиданные» результаты. Может потребоваться добавить
round
вызов к числителю и знаменателю перед выполнением целочисленного деления:Но, если речь идет о работе с фиксированными десятичными знаками (деньги, центы), тогда рассмотрите возможность работы с центами как единицей , так что вся арифметика может быть выполнена как целочисленная арифметика, и конвертировать только в / из основной денежной единицы (доллар) при выполнении I / O.
Или, в качестве альтернативы, используйте библиотеку для десятичных чисел, например, десятичную , которая:
источник
Вы должны принять во внимание, что
//
этоfloor
оператор, и поэтому вы должны сначала подумать, что у вас есть равная вероятность упасть в 100, как в 99 (*) (потому что операция будет100 ± epsilon
приepsilon>0
условии, что шансы получить точно 100,00 ..0 очень низкие.)Вы можете увидеть то же самое со знаком минус,
и вы должны быть удивлены.
С другой стороны,
int(-1/.01)
сначала выполняется деление, а затем применяетсяint()
число, которое является не полом, а усечением до 0 ! Это означает, что в этом случаеследовательно,
Округление, однако, даст ВАШ ожидаемый результат для этого оператора, потому что, опять же, ошибка для этих цифр мала.
(*) Я не говорю, что вероятность одинакова, я просто говорю, что априори, когда вы выполняете такое вычисление с плавающей арифметикой, которая является оценкой того, что вы получаете.
источник
Числа с плавающей запятой не могут точно представлять большинство десятичных чисел, поэтому при вводе литерала с плавающей запятой вы фактически получаете приближение этого литерала. Аппроксимация может быть больше или меньше, чем введенное число.
Вы можете увидеть точное значение числа с плавающей запятой, приведя его к десятичному или дробному числу.
Мы можем использовать тип Fraction, чтобы найти ошибку, вызванную нашим неточным литералом.
Мы также можем узнать, как гранулированные числа с плавающей запятой двойной точности около 100, используя nextafter от numpy.
Исходя из этого, мы можем предположить, что ближайшее число с плавающей запятой
1/0.01000000000000000020816681711721685132943093776702880859375
фактически равно 100.Разница между
1//0.01
иint(1/0.01)
заключается в округлении. 1 // 0.01 округляет точный результат до следующего целого числа за один шаг. Таким образом, мы получаем результат 99.int (1 / 0.01), с другой стороны, округляет в два этапа, сначала округляет результат до ближайшего числа с плавающей запятой двойной точности (которое точно равно 100), затем округляет это число с плавающей запятой до следующего целого числа (которое является опять ровно 100).
источник
int(0.9) == 0
аint(-0.9) == 0
Если вы выполните следующее
Выход будет:
Это то, как оно представлено внутри, поэтому его округление
//
даст99
источник
Decimal(0.01)
вами слишком поздно, ошибка уже закралась, прежде чем позвонитьDecimal
. Я не уверен, как это ответ на вопрос ... Сначала вы должны рассчитать точный 0,01 сDecimal(1) / Decimal(100)
, как я показал в своем ответе.