Округлення, як відомо, це математична операція, яка полягає в заміні числа α наближеним числом α1 із меншою кількістю значущих цифр. Число α1 вибирають так, щоб похибка округлення була якомога меншою.
У різних галузях застосовують різні методи округлення.
На уроках математики в школі традиційно округлюють методом «Округлення до найближчого цілого» (англ. rounding) — це найбільш часто вживаний метод округлення, при якому число округлюється до цілого, модуль різниці з яким у цього числа мінімальний. У загальному випадку, коли число в десятковій системі округляють до N-ого знаку, правило може бути сформульовано таким чином:
якщо N+1 знак < 5, тоді N-ий знак зберігають, а N+1 та всі наступні обнуляють;
якщо N+1 знак ≥ 5, тоді N-ий знак збільшують на одиницю, а N+1 та всі наступні обнуляють;
Наприклад: 11,9 → 12; −0,9 → −1; −1,1 → −1; 2,5 → 3.
Давайте напишемо ще кілька прикладів на даний тип округлення:
Округлюємо до цілого:
1,5 → 2
2,5 → 3
3,5 → 4
4,5 → 5
5,5 → 6
6,5 → 7
Такий метод округлення використовує Python версії 2 і старі програмні компілятори, наприклад Turbo Pascal.
Але це не єдиний метод округлення. Відповідно до стандарту IEEE 754 (і 1985, і 2008 року), основним для комп'ютерних обчислень є інший метод. Він зветься округленням до найближчого парного, банківським, статистичним, данським, Гаусовим округленням. Вважається, що банківське округлення дозволяє зменшити похибки при роботі з великими масивами даних. Подробиці можна дізнатися, завітавши на кілька років на правильну спеціальність гарного університету. Даний метод на сьогодні популярний: наприклад, .NET Math.Round так робить по замовчуванню, цей метод використовує для функції стандартного округлення round() PascalABC.Net і Python 3.
Синтаксично округлення в Python 3 здійснюється функцією round(number, ndigits), де number – число що округлюється, а ndigits - кількість знаків після коми. Наприклад:
>>>round(2.137, 2)
2.14
>>>round(1.5)
2
Ось результат такого округлення:
1,5 → 2
2,5 → 2
3,5 → 4
4,5 → 4
5,5 → 6
6,5 → 6
Саме так округлює функція round() в Python 3.
Але є і великі підводні камені, пов’язані з представленням в пам’яті комп’ютера дійсних чисел. Ось приклад:
>>>round(2.15, 1)
З точки зору округлення до найближчого парного, результат повинен бути 2.2
Python 3 вважає інакше:
>>>round(2.15, 1)
2.1
Це пов’язано з тим, як представлено число 2.15 в машинному поданні, давайте переглянемо, як воно виглядає для, наприклад, 30 знаків після коми:
>>> '%0.30f' % 2.15
'2.149999999999999911182158029987'
Тепер все зрозуміло, в такому випадку логіка округлення Python 3 цілком зрозуміла.
Але сюрпризи при округленні дійсних чисел можуть з’являтися цілком несподівано, наприклад:
>>> round(2.15, 1)
2.1
>>> round(2.1500000000000001, 1)
2.1
>>> round(2.150000000000001, 1)
2.2
Як бачимо, камінців вистачає. Але round() – не єдина функція округлення.
В Python 3, в модулі math реалізовано ще декілька методів округлення:
math.ceil(x) – округлення до найближчого більшого числа
math.floor(x) - округлення до найближчого меншого числа
math.trunc(x) - усікає значення X до цілого.
Ось приклади, що ілюструють роботу даних функцій:
>>> import math
>>> math.ceil(1.4)
2
>>> math.floor(1.4)
1
>>> math.trunc(1.4)
1
>>> math.trunc(1.9)
1
>>> math.floor(-1.9)
-2
>>> math.trunc(-1.9)
-1
Усікти значення до цілого можна і без використання модуля math:
>>> int(1.1)
1
>>> int(1.9)
1
>>> int(-1.1)
-1
>>> int(-1.9)
-1
А для естетів, перфекціоністів, тих, хто хоче забезпечити абсолютну точність, контролювати стратегію округлення і системно керувати золотою рибкою, Python 3 має модуль decimal
Цей неймовірний інструмент має аж вісім варіантів округлення:
ROUND_CEILING - Округлення в сторону позитивної нескінченності. Наприклад, число 2.52 буде округлено до 2.6, а число -2.58 до -2.5
ROUND_DOWN - Округлення в сторону нуля. Наприклад, число 2.58 буде округлено до 2.5, а число -2.58 - до -2.5
ROUND_FLOOR - Округлення в бік негативної нескінченності. Наприклад, число 2.52 буде округлено до 2.5, а число -2.58 до -2.6
ROUND_HALF_DOWN - Округлення в сторону від нуля, якщо частина, що округлюється, більше половини останнього значущого розряду, в іншому випадку округлення буде виконано в сторону нуля. Наприклад, число 2.58 буде округлено до 2.6, число 2.55 буде округлено до 2.5, а число -2.58 до -2.6
ROUND_HALF_EVEN - Те ж, що і ROUND_HALF_DOWN, тільки якщо частина, що округлюється дорівнює точно половині останнього значущого розряду, результат округлюється вниз, якщо попередня цифра парна, і вгору - якщо попередня цифра непарна. Наприклад, число 2.65 буде округлено до 2.6, число 2.55 також буде округлено до 2.6
ROUND_HALF_UP - Те ж, що і ROUND_HALF_DOWN, тільки якщо частина, що округлюється, дорівнює точно половині останнього значущого розряду, результат округлюється в бік від нуля. Наприклад, число 2.55 буде округлено до 2.6, а число -2.55 до -2.6
ROUND_UP - Округлення в бік від нуля. Наприклад, число 2.52 буде округлено до 2.6, а число -2.52 - до -2.6
ROUND_05UP - Округлення в бік від нуля, якщо останній значущий розряд містить цифру 0 або 5, в іншому випадку округлення виконується в сторону нуля. Наприклад, число 2.54 буде округлено до 2.6, число 2.64 також буде округлено до 2.6
Звичайно, всі ці варіанти з’явилися в decimal не просто так - вони мають цілком прикладний сенс. З таким інструментарієм можна реалізувати багато чого, в тому числі і популярне шкільне округлення методом «Округлення до найближчого цілого»:
import decimal as decimal
print(decimal.Decimal('2.5').quantize(decimal.Decimal('1'), rounding=decimal.ROUND_HALF_UP))
print(decimal.Decimal('3.5').quantize(decimal.Decimal('1'), rounding=decimal.ROUND_HALF_UP))
out:
3
4
Ну і можна, звичайно, написати і власну функцію округлення, якщо на то є причини. В будь-якому варіанті тема округлення дійсних чисел в Python 3 має чимало непростих аспектів. І програмістам необхідно уважно враховувати дані особливості як на етапі проектування інформаційних систем, так і при розробці стратегій їх тестування.