Поведение округления Python 3.x
Я только что перечитал Что нового в Python 3.0 и в нем говорится:
Стратегия округления функции round() и тип возврата изменены. Точные полпути теперь округляются до ближайшего четного результата, а не от нуля. (Например, раунд (2.5) теперь возвращает 2, а не 3.)
и документация для тура:
Для встроенных типов, поддерживающих round(), значения округляются до ближайшего кратного 10 от степени минус n; если два множителя одинаково близки, округление выполняется до четного выбора
Итак, под v2.7.3:
In [85]: round(2.5)
Out[85]: 3.0
In [86]: round(3.5)
Out[86]: 4.0
как я и ожидал. Однако сейчас под v3.2.3:
In [32]: round(2.5)
Out[32]: 2
In [33]: round(3.5)
Out[33]: 4
Это кажется нелогичным и противоречит тому, что я понимаю о округлении (и обманываю людей). Английский не является моим родным языком, но пока я не прочитал это, я думал, что знаю, что означает округление:-/ Я уверен, что во время введения v3, должно быть, было некоторое обсуждение этого, но я не смог найти вескую причину в мой поиск.
- Кто-нибудь понимает, почему это было изменено на это?
- Существуют ли другие основные языки программирования (например, C, C++, Java, Perl,...), которые делают такое (для меня непоследовательное) округление?
Что мне здесь не хватает?
ОБНОВЛЕНИЕ: @Li-aungYip комментарий о "округлении банкира" дал мне правильный поисковый запрос / ключевые слова для поиска, и я нашел такой вопрос: почему.NET использует округление банкира по умолчанию? так что я буду читать это внимательно.
14 ответов
В наши дни Python 3.0 считается стандартным методом округления, хотя некоторые языковые реализации пока не работают.
Простая техника "всегда вокруг 0,5 вверх" приводит к небольшому смещению в сторону большего числа. При большом количестве вычислений это может быть значительным. Подход Python 3.0 устраняет эту проблему.
Существует более одного метода округления в общем использовании. IEEE 754, международный стандарт математики с плавающей точкой, определяет пять различных методов округления (по умолчанию используется Python 3.0). И есть другие.
Такое поведение не так широко известно, как должно быть. AppleScript был, если я правильно помню, одним из первых, кто применил этот метод округления. round
Команда в AppleScript на самом деле предлагает несколько вариантов, но по умолчанию используется округление к четному, как в IEEE 754. Очевидно инженер, который реализовал round
Команде так надоели все запросы "заставить работать так, как я учился в школе", что он реализовал только это: round 2.5 rounding as taught in school
является допустимой командой AppleScript.:-)
Вы можете контролировать округление в Py3000, используя модуль Decimal:
>>> decimal.Decimal('3.5').quantize(decimal.Decimal('1'),
rounding=decimal.ROUND_HALF_UP)
>>> Decimal('4')
>>> decimal.Decimal('2.5').quantize(decimal.Decimal('1'),
rounding=decimal.ROUND_HALF_EVEN)
>>> Decimal('2')
>>> decimal.Decimal('3.5').quantize(decimal.Decimal('1'),
rounding=decimal.ROUND_HALF_DOWN)
>>> Decimal('3')
Просто добавлю сюда важное примечание из документации:
https://docs.python.org/dev/library/functions.html
Заметка
Поведение round() для чисел с плавающей точкой может быть удивительным: например, round(2.675, 2) дает 2,67 вместо ожидаемых 2,68. Это не ошибка: это результат того факта, что большинство десятичных дробей не может быть представлено в точности как число с плавающей точкой. См. Арифметика с плавающей точкой: Проблемы и Ограничения для получения дополнительной информации.
Так что не удивляйтесь, если получите следующие результаты в Python 3.2:
>>> round(0.25,1), round(0.35,1), round(0.45,1), round(0.55,1)
(0.2, 0.3, 0.5, 0.6)
>>> round(0.025,2), round(0.035,2), round(0.045,2), round(0.055,2)
(0.03, 0.04, 0.04, 0.06)
Python 3.x округляет.5 значений до соседа, который является четным
assert round(0.5) == 0
assert round(1.5) == 2
assert round(2.5) == 2
import decimal
assert decimal.Decimal('0.5').to_integral_value() == 0
assert decimal.Decimal('1.5').to_integral_value() == 2
assert decimal.Decimal('2.5').to_integral_value() == 2
однако, можно изменить десятичное округление "назад", чтобы всегда округлять.5 вверх, если это необходимо:
decimal.getcontext().rounding = decimal.ROUND_HALF_UP
assert decimal.Decimal('0.5').to_integral_value() == 1
assert decimal.Decimal('1.5').to_integral_value() == 2
assert decimal.Decimal('2.5').to_integral_value() == 3
i = int(decimal.Decimal('2.5').to_integral_value()) # to get an int
assert i == 3
assert type(i) is int
У меня недавно тоже были проблемы с этим. Поэтому я разработал модуль python 3, который имеет 2 функции trueround() и trueround_precision(), которые решают эту проблему и дают то же поведение при округлении, которое использовалось в начальной школе (не округление банкиров). Вот модуль. Просто сохраните код и скопируйте его или импортируйте. Примечание: модуль trueround_precision может изменять поведение округления в зависимости от потребностей в соответствии с флагами ROUND_CEILING, ROUND_DOWN, ROUND_FLOOR, ROUND_HALF_DOWN, ROUND_HALF_EVEN, ROUND_HALF_UP, ROUND_UP и ROUND_05UP в десятичном модуле (см. Эту документацию для модулей). Для функций ниже, смотрите строки документации или используйте help(trueround) и help(trueround_precision), если они скопированы в интерпретатор для дальнейшей документации.
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
def trueround(number, places=0):
'''
trueround(number, places)
example:
>>> trueround(2.55, 1) == 2.6
True
uses standard functions with no import to give "normal" behavior to
rounding so that trueround(2.5) == 3, trueround(3.5) == 4,
trueround(4.5) == 5, etc. Use with caution, however. This still has
the same problem with floating point math. The return object will
be type int if places=0 or a float if places=>1.
number is the floating point number needed rounding
places is the number of decimal places to round to with '0' as the
default which will actually return our interger. Otherwise, a
floating point will be returned to the given decimal place.
Note: Use trueround_precision() if true precision with
floats is needed
GPL 2.0
copywrite by Narnie Harshoe <signupnarnie@gmail.com>
'''
place = 10**(places)
rounded = (int(number*place + 0.5if number>=0 else -0.5))/place
if rounded == int(rounded):
rounded = int(rounded)
return rounded
def trueround_precision(number, places=0, rounding=None):
'''
trueround_precision(number, places, rounding=ROUND_HALF_UP)
Uses true precision for floating numbers using the 'decimal' module in
python and assumes the module has already been imported before calling
this function. The return object is of type Decimal.
All rounding options are available from the decimal module including
ROUND_CEILING, ROUND_DOWN, ROUND_FLOOR, ROUND_HALF_DOWN, ROUND_HALF_EVEN,
ROUND_HALF_UP, ROUND_UP, and ROUND_05UP.
examples:
>>> trueround(2.5, 0) == Decimal('3')
True
>>> trueround(2.5, 0, ROUND_DOWN) == Decimal('2')
True
number is a floating point number or a string type containing a number on
on which to be acted.
places is the number of decimal places to round to with '0' as the default.
Note: if type float is passed as the first argument to the function, it
will first be converted to a str type for correct rounding.
GPL 2.0
copywrite by Narnie Harshoe <signupnarnie@gmail.com>
'''
from decimal import Decimal as dec
from decimal import ROUND_HALF_UP
from decimal import ROUND_CEILING
from decimal import ROUND_DOWN
from decimal import ROUND_FLOOR
from decimal import ROUND_HALF_DOWN
from decimal import ROUND_HALF_EVEN
from decimal import ROUND_UP
from decimal import ROUND_05UP
if type(number) == type(float()):
number = str(number)
if rounding == None:
rounding = ROUND_HALF_UP
place = '1.'
for i in range(places):
place = ''.join([place, '0'])
return dec(number).quantize(dec(place), rounding=rounding)
Надеюсь это поможет,
Narnie
Поведение округления в Python 2 в Python 3.
Добавляем 1 на 15 десятичных знаков. Точность до 15 цифр.
round2=lambda x,y=None: round(x+1e-15,y)
Некоторые случаи:
in: Decimal(75.29 / 2).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)
in: round(75.29 / 2, 2)
out: 37.65 GOOD
in: Decimal(85.55 / 2).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)
in: round(85.55 / 2, 2)
out: 42.77 BAD
Для исправления:
in: round(75.29 / 2 + 0.00001, 2)
out: 37.65 GOOD
in: round(85.55 / 2 + 0.00001, 2)
out: 42.78 GOOD
Если вы хотите больше десятичных знаков, например 4, вы должны добавить (+ 0,0000001).
Работа для меня.
Я предлагаю пользовательскую функцию, которая будет работать для DataFrame:
def dfCustomRound(df, dec):
d = 1 / 10 ** dec
df = round(df, dec + 2)
return (((df % (1 * d)) == 0.5 * d).astype(int) * 0.1 * d * np.sign(df) + df).round(dec)
Оператор округления округляет значение до ближайшего целого числа.
Например:
Если значение больше o.5, то оно округляется до 1
print(round(211.5554, 2)) // output is 211.56
Если значение меньше 0,5, оно округляется до 0
print(round(211.5544, 2)) // output is 211.55
Редактировать:
Ранее упомянутый оператор // не используется для округления, он используется, например, для обработки плавающего вывода деления.
print(10//3) // output is 3 instead of 3.3333333333333335
Образец репродукции:
['{} => {}'.format(x+0.5, round(x+0.5)) for x in range(10)]
['0.5 => 0', '1.5 => 2', '2.5 => 2', '3.5 => 4', '4.5 => 4', '5.5 => 6', '6.5 => 6', '7.5 => 8', '8.5 => 8', '9.5 => 10']
API: https://docs.python.org/3/library/functions.html
Состояния:
Возвращаемое число, округленное до точности ndigits после десятичной точки. Если ndigits опущен или равен None, он возвращает ближайшее целое число на свой вход.
Для встроенных типов, поддерживающих round(), значения округляются до ближайшего кратного 10 к степени минус ndigits; если два мультипликатора одинаково близки, округление выполняется до четного выбора (например, как раунд (0.5), так и раунд (-0.5) равны 0, а раунд (1.5) равен 2). Любое целочисленное значение допустимо для ndigits (положительное, нулевое или отрицательное). Возвращаемое значение является целым числом, если ndigits опущено или отсутствует. В противном случае возвращаемое значение имеет тот же тип, что и число.
Для общего номера объекта Python округлите делегатов до числа.круглый
Замечание Поведение round () для чисел с плавающей точкой может быть удивительным: например, round(2.675, 2) дает 2,67 вместо ожидаемых 2,68. Это не ошибка: это результат того факта, что большинство десятичных дробей не может быть представлено в точности как число с плавающей точкой. Посмотрите Арифметику с плавающей запятой: Проблемы и Ограничения для получения дополнительной информации.
Учитывая это понимание, вы можете использовать некоторую математику, чтобы решить ее
import math
def my_round(i):
f = math.floor(i)
return f if i - f < 0.5 else f+1
теперь вы можете запустить тот же тест с my_round вместо round.
['{} => {}'.format(x + 0.5, my_round(x+0.5)) for x in range(10)]
['0.5 => 1', '1.5 => 2', '2.5 => 3', '3.5 => 4', '4.5 => 5', '5.5 => 6', '6.5 => 7', '7.5 => 8', '8.5 => 9', '9.5 => 10']
# round module within numpy when decimal is X.5 will give desired (X+1)
import numpy as np
example_of_some_variable = 3.5
rounded_result_of_variable = np.round(example_of_some_variable,0)
print (rounded_result_of_variable)
Самый простой способ округления в Python 3.x, как его учат в школе, - это использовать вспомогательную переменную:
n = 0.1
round(2.5 + n)
И это будут результаты серий от 2.0 до 3.0 (с шагом 0,1):
>>> round(2 + n)
>>> 2
>>> round(2.1 + n)
>>> 2
>>> round(2.2 + n)
>>> 2
>>> round(2.3 + n)
>>> 2
>>> round(2.4 + n)
>>> 2
>>> round(2.5 + n)
>>> 3
>>> round(2.6 + n)
>>> 3
>>> round(2.7 + n)
>>> 3
>>> round(2.8 + n)
>>> 3
>>> round(2.9 + n)
>>> 3
>>> round(3 + n)
>>> 3
Вы можете контролировать округление с помощью модуля math.ceil:
import math
print(math.ceil(2.5))
> 3
Попробуйте этот код:
def roundup(input):
demo = input if str(input)[-1] != "5" else str(input).replace("5","6")
place = len(demo.split(".")[1])-1
return(round(float(demo),place))
Результат будет:
>>> x = roundup(2.5)
>>> x
3.0
>>> x = roundup(2.05)
>>> x
2.1
>>> x = roundup(2.005)
>>> x
2.01