Подтвердить что ты не робот

Круглый поплавок до x десятичных знаков?

Есть ли способ округлить python float до x десятичных знаков? Например:

>>> x = roundfloat(66.66666666666, 4)
66.6667
>>>x = roundfloat(1.29578293, 6)
1.295783

Я нашел способы обрезать/усекать их (66.666666666 → 66.6666), но не раунд (66.666666666 → 66.6667).

4b9b3361

Ответ 1

Используйте встроенную функцию round():

In [23]: round(66.66666666666,4)
Out[23]: 66.6667

In [24]: round(1.29578293,6)
Out[24]: 1.295783

help round():

round (number [, ndigits]) → число с плавающей запятой

округлить число до заданной точности в десятичных разрядах (по умолчанию 0 цифр). Это всегда возвращает число с плавающей запятой. Точность может быть отрицательным.

Ответ 2

Я чувствую себя вынужденным предоставить контрапункт ответа Ашвини Чаудхари. Несмотря на видимость, форма с двумя аргументами функции round не обходит плавающий Python до заданного количества десятичных знаков, и часто это не решение, которое вы хотите, даже если вы так думаете. Позвольте мне объяснить...

Возможность округлять (Python) float до некоторого количества десятичных знаков - это то, что часто запрашивается, но оказывается редко, что действительно необходимо. Простой ответ round(x, number_of_places) - это что-то вроде привлекательной неприятности: похоже, что он делает то, что вы хотите, но благодаря тому, что поплавки Python хранятся внутри двоично, он делает что-то более тонкое. Рассмотрим следующий пример:

>>> round(52.15, 1)
52.1

С наивным пониманием того, что делает round, это выглядит неправильно: конечно, оно должно округляться до 52.2, а не вниз до 52.1? Чтобы понять, почему на такое поведение нельзя полагаться, вы должны понимать, что, хотя это выглядит как простая операция с десятичной точностью, это далеко не просто.

Итак, вот что на самом деле происходит в приведенном выше примере. (глубокое дыхание) Мы показываем десятичное представление ближайшего двоичного числа с плавающей запятой до ближайшего десятичного числа n -digits-after-to-point до двоичного приближения с плавающей запятой числового литерала, записанного в десятичном значении. Таким образом, чтобы получить от исходного числового литерала до отображаемого вывода, базовый механизм сделал четыре отдельных преобразования между двоичным и десятичным форматами, по два в каждом направлении. Разрушая его (и с обычными заявлениями о предполагаемом формате бинарного формата IEEE 754, округлении до четного округления и правилах IEEE 754):

  • Сначала числовой литерал 52.15 обрабатывается и преобразуется в плавающий Python. Фактическое сохраненное количество: 7339460017730355 * 2**-47 или 52.14999999999999857891452847979962825775146484375.

  • Внутренне как первый шаг операции round, Python вычисляет ближайшую десятичную строку с десятичной цифрой после запятой к сохраненному номеру. Поскольку это сохраненное число является касанием под исходным значением 52.15, мы заканчиваем округление и получаем строку 52.1. Это объясняет, почему мы получаем 52.1 как окончательный вывод вместо 52.2.

  • Затем на втором этапе операции round Python превращает эту строку обратно в float, получая ближайший двоичный номер с плавающей запятой до 52.1, который теперь 7332423143312589 * 2**-47 или 52.10000000000000142108547152020037174224853515625.

  • Наконец, как часть цикла чтения-eval-печати Python (REPL), отображается значение с плавающей запятой (в десятичной форме). Это включает преобразование двоичного значения обратно в десятичную строку, получив 52.1 в качестве конечного результата.

В Python 2.7 и более поздних версиях мы имеем приятную ситуацию, когда два преобразования на шагах 3 и 4 отменяют друг друга. Это связано с выбором Python реализации repr, которая дает кратчайшее десятичное значение, гарантированное правильное округление до фактического поплавка. Одним из следствий этого выбора является то, что если вы начинаете с любого (не слишком большого, не слишком малого) десятичного литерала с 15 или менее значащими цифрами, тогда будет отображаться соответствующий поплавок с указанием тех же самых цифр:

>>> x = 15.34509809234
>>> x
15.34509809234

К сожалению, это приводит к иллюзии, что Python хранит значения в десятичной форме. Однако не в Python 2.6! Здесь исходный пример выполнен в Python 2.6:

>>> round(52.15, 1)
52.200000000000003

Мы не только обходим в обратном направлении, получаем 52.2 вместо 52.1, но отображаемое значение даже не печатается как 52.2! Такое поведение вызвало многочисленные сообщения для трекера Python по строкам "раунд сломан!". Но это не round, что сломано, это ожидания пользователя. (Хорошо, хорошо, round немного сломан в Python 2.6, поскольку он не использует правильное округление.)

Короткий вариант: если вы используете раунд с двумя аргументами, и вы ожидаете предсказуемого поведения от двоичного приближения к десятичному раунду двоичного приближения к десятичному полуприцепу, вы просите о неприятности.

Достаточно, чтобы аргумент "аргумент с двумя аргументами был плохим". Что вы должны использовать вместо этого? Есть несколько возможностей, в зависимости от того, что вы пытаетесь сделать.

  • Если вы округляете для целей показа, то вам совсем не нужен результат поплавка; вам нужна строка. В этом случае ответ заключается в использовании форматирования строк:

    >>> format(66.66666666666, '.4f')
    '66.6667'
    >>> format(1.29578293, '.6f')
    '1.295783'
    

    Даже тогда нужно знать внутреннее двоичное представление, чтобы не удивляться поведению кажущихся десятичных полуприцепов.

    >>> format(52.15, '.1f')
    '52.1'
    
  • Если вы работаете в контексте, где важно, в каком направлении округлены десятичные числа, округленные (например, в некоторых финансовых контекстах), вы можете представить свои числа с помощью типа Decimal. Выполнение десятичного раунда по типу Decimal имеет гораздо больший смысл, чем двоичный тип (одинаково, округление до фиксированного числа двоичных мест имеет смысл в двоичном типе). Кроме того, модуль Decimal дает вам лучший контроль над режимом округления. В Python 3, round выполняет работу напрямую. В Python 2 вам понадобится метод quantize.

    >>> Decimal('66.66666666666').quantize(Decimal('1e-4'))
    Decimal('66.6667')
    >>> Decimal('1.29578293').quantize(Decimal('1e-6'))
    Decimal('1.295783')
    
  • В редких случаях версия с двумя аргументами round действительно является тем, что вы хотите: возможно, вы загружаете биннинг в ячейки размером 0.01, и вам не особо важно, каким образом граничные случаи идти. Однако эти случаи редки, и трудно обосновать существование двух аргументной версии встроенного round, основанного только на тех случаях.