Сегодня еще один новичок в Python был введен в заблуждение ненужными статьями в китайских технических блогах.
Вопрос начинающего:
В Python, как мне точно округлить число с плавающей запятой с двумя десятичными знаками?
Если вы выполните поиск в Google или Baidu, вы найдете множество статей из CSDN, Baijiahao, Toutiaohao или Jianshu, но они не говорят ничего, кроме следующего:
Бредовые статьи, в которых даже нет примеров
Как показано на картинке ниже, мне лень жаловаться.
Используйте функцию округления
Примеры они приводят:
>>> round(1.234, 2)
1.23
Такого рода статьи он только продемонстрировал四舍
, но без демо五入
. Итак, если вы немного измените код, вы обнаружите, что есть проблема:
>>> round(11.245, 2)
11.24
Увеличить, затем уменьшить
Этот вид статьи немного лучше, я знаю, чтобы привести еще несколько примеров:
Тем не менее, такого рода статьи также полны лазеек.Пока вы попробуете еще несколько чисел, вы обнаружите проблему.В Python 2 и Python 3 эффект отличается. Давайте посмотрим на эффект запуска следующего Python 2:
В Python 2 используйте напрямуюround
,1.125
С точностью до двух знаков после запятой, как1.13
,и1.115
С точностью до двух знаков после запятой1.11
.
Давайте посмотрим на следующий эффект в Python 3:
Под Python 3,1.125
После точности до двух знаков после запятой1.12
.
Его пример увеличения, а затем уменьшения масштаба в Python 3 не всегда корректен.
Делать вид
Есть еще своего рода вынужденный товар.Статья похожа на увеличение и уменьшение масштаба, но он все равно знаетdecimal
этот модуль.
Но как он это использует, давайте посмотрим на него
具体原因不详
? ? ? ?
不推荐使用这个方法
? ? ?
Такому человеку нужно сначала установить принудительно, говоря, что он знает, что есть такая библиотека, но обнаруживает, что при ее использовании возникает проблема, и он не знает причины, поэтому не всем рекомендуется использовать Это.
Decimal это модуль специально используемый для высокоточных вычислений.Он даже сказал что не всем рекомендуется его использовать? ? ?
Что не так с круглым?
Поругавшись, поговорим о, в Python 3,round
Что именно не так с этой встроенной функцией.
Некоторые люди сказали в Интернете, потому что в компьютере десятичные дроби, например, неточны1.115
В компьютере это на самом деле1.1149999999999999911182
, поэтому, когда вы точны до двух знаков после запятой для этой десятичной точки, на самом деле третий знак после запятой4
, так округлил, значит результат1.11
.
Это утверждение верно наполовину.
Потому что не все десятичные числа неточны в компьютерах. Например0.125
Это десятичное число является точным в компьютере, это0.125
, не опуская следующего значения, без приближения, действительно0.125
.
Но если мы вставим Python0.125
с точностью до двух знаков после запятой, тогда становится0.12
:
>>> round(0.125, 2)
0.12
почему ты здесь四舍
уже?
И, что еще более странно, еще одна десятичная дробь, которую можно точно представить в компьютере.0.375
, давайте посмотрим, насколько он точен до двух знаков после запятой:
>>> round(0.375, 2)
0.38
почему снова здесь五入
уже?
Поскольку в Python 3round
Точность десятичных знаков использует四舍六入五成双
Путь.
Если вы написали отчет об эксперименте по физике в университете, вы должны помнить, что преподаватель сказал, что если вы используете округление напрямую, конечный результат может быть высоким. Так что нужно использовать奇进偶舍
способ обработки.
Например, для десятичнойa.bcd
, нужно быть точным до двух знаков после запятой, то нужно смотреть на третий знак после запятой:
- если
d
Если он меньше 5, он будет сразу отброшен. - если
d
Больше 5, прямой перенос - если
d
равно 5:-
d
За ним нет данных, а c равно偶数
, тогда не переносите, держите c -
d
За ним нет данных, а c равно奇数
, затем перенесите, c становится (c + 1) - если
d
Есть также ненулевые цифры позади, например, фактическое десятичное числоa.bcdef
, в это время нужно нести, c становится (c + 1)
-
Что касается нечетных и четных домов, заинтересованные студенты могут найти эти две записи в Википедии:数值修约
и奇进偶舍
.
так,round
Если результат не такой, как вы ожидали, то вам нужно рассмотреть две причины:
- Можно ли точно сохранить эту десятичную дробь в компьютере? Если это невозможно, то, вероятно, он неправильно округляется, например.
1.115
, его третье десятичное место на самом деле4
, конечно, будет отброшено. - Если ваше десятичное число может быть точно представлено на компьютере, то
round
Используемый механизм переноски奇进偶舍
, так что это зависит от того, какой бит вы хотите сохранить, будь то нечетный или четный, и есть ли данные после его следующего бита.
Как правильно сделать округление
Если мы хотим добиться нашего математического округления, нам нужно использовать десятичный модуль.
Как правильно использовать десятичный модуль?
Читайте официальную документацию, не читайте китайские спам-блоги! ! !
Читайте официальную документацию, не читайте китайские спам-блоги! ! !
Читайте официальную документацию, не читайте китайские спам-блоги! ! !
Не беспокойтесь о том, что вы не понимаете английский, Python выпустил официальную китайскую документацию (использование некоторых функций еще не переведено).
Давайте взглянем:docs.Python.org/this-talent/3/неделя…
Официальный документ дает конкретный метод написания:
>>>Decimal('1.41421356').quantize(Decimal('1.000'))
Decimal('1.414')
Итак, давайте проверим это,0.125
и0.375
Какие два десятичных знака:
>>> from decimal import Decimal
>>> Decimal('0.125').quantize(Decimal('0.00'))
Decimal('0.12')
>>> Decimal('0.375').quantize(Decimal('0.00'))
Decimal('0.38')
как результат иround
Такой же? Давайте посмотрим на документациюquantize
В прототипе функции и документации указано:
Здесь упоминается, что вы можете указатьrounding
параметр для определения метода переноса. Если не указаноrounding
параметр, то метод переноса, предоставляемый контекстом, используется по умолчанию.
Теперь давайте посмотрим, что такое метод переноса в контексте по умолчанию:
>>> from decimal import getcontext
>>> getcontext().rounding
'ROUND_HALF_EVEN'
Как показано ниже:
ROUND_HALF_EVEN
фактически奇进偶舍
! Если мы хотим указать истинное округление, то нам нужно указать вquantize
Указанный метод переносаROUND_HALF_UP
:
>>> from decimal import Decimal, ROUND_HALF_UP
>>> Decimal('0.375').quantize(Decimal('0.00'), rounding=ROUND_HALF_UP)
Decimal('0.38')
>>> Decimal('0.125').quantize(Decimal('0.00'), rounding=ROUND_HALF_UP)
Decimal('0.13')
Кажется, теперь все работает нормально.
Так кто-нибудь спросит дальше, а что, если параметр Decimal получает не строку, а число с плавающей запятой?
Давайте поэкспериментируем:
>>> Decimal(0.375).quantize(Decimal('0.00'), rounding=ROUND_HALF_UP)
Decimal('0.38')
>>> Decimal(0.125).quantize(Decimal('0.00'), rounding=ROUND_HALF_UP)
Decimal('0.13')
Означает ли это, что в первом параметре Decimal можно напрямую передавать числа с плавающей запятой?
Давайте изменим число для проверки:
>>> Decimal(11.245).quantize(Decimal('0.00'), rounding=ROUND_HALF_UP)
Decimal('11.24')
>>> Decimal('11.245').quantize(Decimal('0.00'), rounding=ROUND_HALF_UP)
Decimal('11.25')
почему числа с плавающей запятой11.245
и строка'11.245'
, После передачи результат другой?
Продолжаем искать ответы в документации.
В официальной документации четко указано, что если параметр, который вы передаете, является числом с плавающей запятой, и значение с плавающей запятой не может быть точно сохранено в компьютере, оно сначала будет преобразовано в неточное двоичное значение, а затем это неточное двоичное значение. значения преобразуются в等效的十进制值
.
Для десятичного числа, которое не может быть точно представлено, когда вы передаете его, до того, как Python получит число, число было преобразовано в неточное число. Итак, хотя вы передаете параметр как11.245
, но то, что получает Python, на самом деле11.244999999999...
.
Но если вы передадите строку'11.245'
, то когда Python его получает, он знает, что это11.245
, не будет заранее конвертирован в неточное значение, поэтому рекомендуется указатьDecimal
Первый параметр строкового типа числа с плавающей запятой вместо прямой записи числа с плавающей запятой.
Таким образом, если вы хотите добиться точного округления, код должен быть написан следующим образом:
from decimal import Decimal, ROUND_HALF_UP
origin_num = Decimal('11.245')
answer_num = origin_num.quantize(Decimal('0.00'), rounding=ROUND_HALF_UP)
print(answer_num)
Эффект операции показан на следующем рисунке:
В частности, обратите внимание, что после выполнения точных вычислений числа с плавающей запятой больше не должны использоваться отдельно, а должны использоваться всегда.Decimal('浮点数')
. В противном случае при присвоении значения точность теряется.Рекомендуется использовать Decimal, например, везде:
a = Decimal('0.1')
b = Decimal('0.2')
c = a + b
print(c)
Наконец, если некоторые студенты хотят знать, почему 0,125 и 0,375 могут быть сохранены точно, но 1,115 и 11,245 не могут быть точно сохранены, пожалуйста, оставьте сообщение под этой статьей.Если есть много студентов, которые хотят знать, я напишу статью, чтобы объяснять.
В конце концов, если вы не понимаете по-английски, а китайские документы слишком скучны, то используйте Nuggets. Качественные китайские статьи Наггетс в 10 000 раз выше, чем у CSDN.
Если статья была вам полезна, рассмотрите возможность подписаться на мою общедоступную учетную запись WeChat: Unwen Code (ID: istkingname)