Округлення десяткових і цілих чисел у Python за допомогою “round” і “Decimal.quantize”

Бізнес

Нижче пояснюється, як округлити числа в Python шляхом округлення або округлення до парного числа. Передбачається, що числа мають тип float з плаваючою комою або ціле число int.

  • вбудована функція (наприклад, на мові програмування):round()
    • Округліть десяткові числа до будь-якої кількості цифр.
    • Округлити цілі числа до будь-якої кількості цифр.
    • round() округляє до парного числа, а не до звичайного округлення
  • стандартна бібліотекаdecimalquantize()
    • DecimalСтворення об’єкта
    • Округлення десяткових чисел до будь-якої кількості цифр і округлення до парних чисел
    • Округлення цілих чисел до будь-якої кількості цифр і округлення до парних чисел
  • Визначте нову функцію
    • Округлюйте десяткові числа до будь-якої кількості цифр.
    • Округлити цілі числа до будь-якої кількості цифр
    • Примітка: для від’ємних значень

Зауважте, що, як згадувалося вище, вбудована функція round — це не загальне округлення, а округлення до парного числа. Подробиці дивіться нижче.

вбудована функція (наприклад, на мові програмування):round()

Round() надається як вбудована функція. Його можна використовувати без імпорту будь-яких модулів.

Перший аргумент — це вихідне число, а другий — кількість цифр (до скільки цифр округлити).

Округліть десяткові числа до будь-якої кількості цифр.

Нижче наведено приклад обробки для типу з плаваючою комою.

Якщо другий аргумент опущено, він округляється до цілого числа. Тип також стає цілочисельним типом int.

f = 123.456

print(round(f))
# 123

print(type(round(f)))
# <class 'int'>

Якщо вказано другий аргумент, він повертає тип з плаваючою комою.

Якщо вказано додатне ціле число, вказується десятковий знак; якщо вказано від’ємне ціле число, вказується ціле місце. -1 округлює до найближчої десятої, -2 округлює до найближчої сотої і 0 округляє до цілого числа (перше місце), але повертає тип з плаваючою чисельністю, на відміну від опущеного.

print(round(f, 1))
# 123.5

print(round(f, 2))
# 123.46

print(round(f, -1))
# 120.0

print(round(f, -2))
# 100.0

print(round(f, 0))
# 123.0

print(type(round(f, 0)))
# <class 'float'>

Округлити цілі числа до будь-якої кількості цифр.

Нижче наведено приклад обробки цілого типу int.

Якщо другий аргумент опущено, або якщо вказано 0 або додатне ціле число, вихідне значення повертається як є. Якщо вказано від’ємне ціле число, воно округляється до відповідного цілого розряду. В обох випадках повертається цілочисельний тип int.

i = 99518

print(round(i))
# 99518

print(round(i, 2))
# 99518

print(round(i, -1))
# 99520

print(round(i, -2))
# 99500

print(round(i, -3))
# 100000

round() округляє до парного числа, а не до звичайного округлення

Зауважте, що округлення за допомогою вбудованої функції round() у Python 3 округлює до парного числа, а не до загального округлення.

Як написано в офіційній документації, 0,5 округляється до 0, 5 – до 0 і так далі.

print('0.4 =>', round(0.4))
print('0.5 =>', round(0.5))
print('0.6 =>', round(0.6))
# 0.4 => 0
# 0.5 => 0
# 0.6 => 1

print('4 =>', round(4, -1))
print('5 =>', round(5, -1))
print('6 =>', round(6, -1))
# 4 => 0
# 5 => 0
# 6 => 10

Визначення округлення до парного числа таке.

Якщо дріб менший за 0,5, округляємо його в меншу сторону; якщо частка більше 0,5, округляємо її в більшу сторону; якщо дріб дорівнює точно 0,5, округлюйте його до парного числа між округленням у меншу і більшу сторону.
Rounding – Wikipedia

0,5 не завжди скорочується.

print('0.5 =>', round(0.5))
print('1.5 =>', round(1.5))
print('2.5 =>', round(2.5))
print('3.5 =>', round(3.5))
print('4.5 =>', round(4.5))
# 0.5 => 0
# 1.5 => 2
# 2.5 => 2
# 3.5 => 4
# 4.5 => 4

У деяких випадках визначення округлення до парного числа навіть не стосується обробки після двох знаків після коми.

print('0.05 =>', round(0.05, 1))
print('0.15 =>', round(0.15, 1))
print('0.25 =>', round(0.25, 1))
print('0.35 =>', round(0.35, 1))
print('0.45 =>', round(0.45, 1))
# 0.05 => 0.1
# 0.15 => 0.1
# 0.25 => 0.2
# 0.35 => 0.3
# 0.45 => 0.5

Це пов’язано з тим, що десяткові дроби не можуть бути представлені в точності як числа з плаваючою комою, як зазначено в офіційній документації.

Поведінка round() для чисел з плаваючою комою може вас здивувати:Наприклад, round(2,675, 2) дасть вам 2,67 замість 2,68, як очікувалося. Це не помилка.:Це є результатом того факту, що більшість десяткових дробів не можуть бути точно представлені числами з плаваючою комою.
round() — Built-in Functions — Python 3.10.2 Documentation

Якщо ви хочете досягти загального округлення або точного округлення десяткових дробів до парних чисел, ви можете використовувати стандартну бібліотеку десяткового квантування (описано нижче) або визначити нову функцію.

Також зауважте, що round() у Python 2 не округлює до парного числа, а округлює.

quantize() десяткової стандартної бібліотеки

Десятковий модуль стандартної бібліотеки можна використовувати для обробки точних десяткових чисел з плаваючою комою.

Використовуючи метод quantize() десяткового модуля, можна округлити числа, вказавши режим округлення.

Встановлені значення для округлення аргументів методу quantize() мають такі значення відповідно.

  • ROUND_HALF_UP:Загальне заокруглення
  • ROUND_HALF_EVEN:Округлення до парних чисел

Десятковий модуль є стандартною бібліотекою, тому додаткового встановлення не потрібно, але потрібен імпорт.

from decimal import Decimal, ROUND_HALF_UP, ROUND_HALF_EVEN

Створення десяткового об’єкта

Decimal() можна використовувати для створення об’єктів типу Decimal.

Якщо вказати тип з плаваючою точкою як аргумент, ви зможете побачити, як це значення насправді розглядається.

print(Decimal(0.05))
# 0.05000000000000000277555756156289135105907917022705078125

print(type(Decimal(0.05)))
# <class 'decimal.Decimal'>

Як показано в прикладі, 0,05 не розглядається як точно 0,05. Це причина, чому описана вище вбудована функція round() округляла значення, відмінне від очікуваного для десяткових значень, включаючи 0,05 у прикладі.

Оскільки 0,5 є половиною (-1 ступінь 2), його можна точно виразити у двійковій формі.

print(Decimal(0.5))
# 0.5

Якщо ви вкажете тип рядка str замість типу float, він буде розглядатися як десятковий тип точного значення.

print(Decimal('0.05'))
# 0.05

Округлення десяткових чисел до будь-якої кількості цифр і округлення до парних чисел

Викликати quantize() з об’єкта типу Decimal, щоб округлити значення.

Першим аргументом quantize() є рядок з такою ж кількістю цифр, що й кількість цифр, яку ви хочете знайти, наприклад «0,1» або «0,01».

Крім того, аргумент ROUNDING визначає режим округлення; якщо вказано ROUND_HALF_UP, використовується загальне округлення.

f = 123.456

print(Decimal(str(f)).quantize(Decimal('0'), rounding=ROUND_HALF_UP))
# 123

print(Decimal(str(f)).quantize(Decimal('0.1'), rounding=ROUND_HALF_UP))
# 123.5

print(Decimal(str(f)).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP))
# 123.46

На відміну від вбудованої функції round(), 0,5 округляється до 1.

print('0.4 =>', Decimal(str(0.4)).quantize(Decimal('0'), rounding=ROUND_HALF_UP))
print('0.5 =>', Decimal(str(0.5)).quantize(Decimal('0'), rounding=ROUND_HALF_UP))
print('0.6 =>', Decimal(str(0.6)).quantize(Decimal('0'), rounding=ROUND_HALF_UP))
# 0.4 => 0
# 0.5 => 1
# 0.6 => 1

Якщо для округлення аргументу встановлено значення ROUND_HALF_EVEN, округлення виконується до парних чисел, як у вбудованій функції round().

Як згадувалося вище, якщо в якості аргументу Decimal() вказано тип з плаваючою комою, він розглядається як об’єкт Decimal зі значенням, рівним фактичному значенню типу float, тому результат використання quantize() Метод буде відрізнятися від очікуваного, як і вбудована функція round().

print('0.05 =>', round(0.05, 1))
print('0.15 =>', round(0.15, 1))
print('0.25 =>', round(0.25, 1))
print('0.35 =>', round(0.35, 1))
print('0.45 =>', round(0.45, 1))
# 0.05 => 0.1
# 0.15 => 0.1
# 0.25 => 0.2
# 0.35 => 0.3
# 0.45 => 0.5

print('0.05 =>', Decimal(0.05).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
print('0.15 =>', Decimal(0.15).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
print('0.25 =>', Decimal(0.25).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
print('0.35 =>', Decimal(0.35).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
print('0.45 =>', Decimal(0.45).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
# 0.05 => 0.1
# 0.15 => 0.1
# 0.25 => 0.2
# 0.35 => 0.3
# 0.45 => 0.5

Якщо аргумент Decimal() вказано як рядок типу str, він розглядається як об’єкт Decimal саме з таким значенням, тож результат буде відповідним очікуванням.

print('0.05 =>', Decimal(str(0.05)).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
print('0.15 =>', Decimal(str(0.15)).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
print('0.25 =>', Decimal(str(0.25)).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
print('0.35 =>', Decimal(str(0.35)).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
print('0.45 =>', Decimal(str(0.45)).quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN))
# 0.05 => 0.0
# 0.15 => 0.2
# 0.25 => 0.2
# 0.35 => 0.4
# 0.45 => 0.4

Оскільки 0,5 можна правильно обробити за допомогою типу float, немає проблем із зазначенням типу float як аргументу Decimal() під час округлення до цілого, але безпечніше вказати тип string string при округленні до десяткового знака.

Наприклад, 2,675 насправді дорівнює 2,67499…. у типу float. Тому, якщо ви хочете округлити до двох знаків після коми, ви повинні вказати рядок до Decimal(), інакше результат буде відрізнятися від очікуваного результату, незалежно від того, чи ви округлите до найближчого цілого (ROUND_HALF_UP) чи до парного числа (ROUND_HALF_EVEN ).

print(Decimal(2.675))
# 2.67499999999999982236431605997495353221893310546875

print(Decimal(2.675).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP))
# 2.67

print(Decimal(str(2.675)).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP))
# 2.68

print(Decimal(2.675).quantize(Decimal('0.01'), rounding=ROUND_HALF_EVEN))
# 2.67

print(Decimal(str(2.675)).quantize(Decimal('0.01'), rounding=ROUND_HALF_EVEN))
# 2.68

Зауважте, що метод quantize() повертає число типу Decimal, тому, якщо ви хочете оперувати числом типу float, вам потрібно перетворити його на тип float за допомогою float(), інакше виникне помилка.

d = Decimal('123.456').quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)

print(d)
# 123.46

print(type(d))
# <class 'decimal.Decimal'>

# print(1.2 + d)
# TypeError: unsupported operand type(s) for +: 'float' and 'decimal.Decimal'

print(1.2 + float(d))
# 124.66

Округлення цілих чисел до будь-якої кількості цифр і округлення до парних чисел

Якщо ви хочете округлити до цілого розряду, вказавши щось на кшталт «10» як перший аргумент, ви не отримаєте бажаного результату.

i = 99518

print(Decimal(i).quantize(Decimal('10'), rounding=ROUND_HALF_UP))
# 99518

Це пов’язано з тим, що quantize() виконує округлення відповідно до показника об’єкта Decimal, але показник Decimal(’10’) дорівнює 0, а не 1.

Ви можете вказати довільний показник, використовуючи E як рядок показника (наприклад, ‘1E1’). Показник ступеня можна перевірити за допомогою методу as_tuple.

print(Decimal('10').as_tuple())
# DecimalTuple(sign=0, digits=(1, 0), exponent=0)

print(Decimal('1E1').as_tuple())
# DecimalTuple(sign=0, digits=(1,), exponent=1)

Таким чином, результат буде в експоненційному позначенні з використанням E. Якщо ви хочете використовувати звичайне позначення, або якщо ви хочете оперувати цілочисельним типом int після округлення, використовуйте int() для перетворення результату.

print(Decimal(i).quantize(Decimal('1E1'), rounding=ROUND_HALF_UP))
# 9.952E+4

print(int(Decimal(i).quantize(Decimal('1E1'), rounding=ROUND_HALF_UP)))
# 99520

print(int(Decimal(i).quantize(Decimal('1E2'), rounding=ROUND_HALF_UP)))
# 99500

print(int(Decimal(i).quantize(Decimal('1E3'), rounding=ROUND_HALF_UP)))
# 100000

Якщо для округлення аргументу встановлено значення ROUND_HALF_UP, відбудеться загальне округлення, наприклад, 5 буде округлено до 10.

print('4 =>', int(Decimal(4).quantize(Decimal('1E1'), rounding=ROUND_HALF_UP)))
print('5 =>', int(Decimal(5).quantize(Decimal('1E1'), rounding=ROUND_HALF_UP)))
print('6 =>', int(Decimal(6).quantize(Decimal('1E1'), rounding=ROUND_HALF_UP)))
# 4 => 0
# 5 => 10
# 6 => 10

Звичайно, немає ніяких проблем, якщо ви вкажете його як рядок.

Визначте нову функцію

Метод використання десяткового модуля є точним і безпечним, але якщо ви не влаштовуєте перетворення типів, ви можете визначити нову функцію для досягнення загального округлення.

Існує багато можливих способів зробити це, наприклад, наступна функція.

def my_round(val, digit=0):
    p = 10 ** digit
    return (val * p * 2 + 1) // 2 / p

Якщо вам не потрібно вказувати кількість цифр і завжди округлювати до першого десяткового знака, ви можете використовувати простішу форму.

my_round_int = lambda x: int((x * 2 + 1) // 2)

Якщо вам потрібно бути точним, безпечніше використовувати десятковий.

Нижче наведено лише для довідки.

Округлюйте десяткові числа до будь-якої кількості цифр.

print(int(my_round(f)))
# 123

print(my_round_int(f))
# 123

print(my_round(f, 1))
# 123.5

print(my_round(f, 2))
# 123.46

На відміну від раунду, 0,5 стає 1 відповідно до загального округлення.

print(int(my_round(0.4)))
print(int(my_round(0.5)))
print(int(my_round(0.6)))
# 0
# 1
# 1

Округлити цілі числа до будь-якої кількості цифр

i = 99518

print(int(my_round(i, -1)))
# 99520

print(int(my_round(i, -2)))
# 99500

print(int(my_round(i, -3)))
# 100000

На відміну від круглого, 5 стає 10 відповідно до звичайного округлення.

print(int(my_round(4, -1)))
print(int(my_round(5, -1)))
print(int(my_round(6, -1)))
# 0
# 10
# 10

Примітка: для від’ємних значень

У наведеному вище прикладі функції -0,5 округлюється до 0.

print(int(my_round(-0.4)))
print(int(my_round(-0.5)))
print(int(my_round(-0.6)))
# 0
# 0
# -1

Існують різні способи заокруглення для від’ємних значень, але якщо ви хочете перетворити -0,5 на -1, ви можете змінити це таким чином, наприклад

import math

def my_round2(val, digit=0):
    p = 10 ** digit
    s = math.copysign(1, val)
    return (s * val * p * 2 + 1) // 2 / p * s

print(int(my_round2(-0.4)))
print(int(my_round2(-0.5)))
print(int(my_round2(-0.6)))
# 0
# -1
# -1
Copied title and URL