Округлення в третьому Python – заплутана математика

Округлення, як відомо, це  математична операція, яка полягає в заміні числа α наближеним числом α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 має чимало непростих аспектів. І програмістам необхідно уважно враховувати дані особливості як на етапі проектування інформаційних систем, так і при розробці стратегій їх тестування.

 

 


 

Вхід для своїх