Контекст: якщо ви перший раз спробуєте попрацювати із вбудованою функцією round то вас може збентежити що вона працює не так як вас вчили округленню у школі (особливо якщо ви ніколи й на інших мовах не намагались цього зробити). У школі нас вчили що числа які закінчуються на .4 округлюються у меншу, а на .5 у більшу сторону, але пайтон робить щось інше round(1.5) == 2 але round(2.5) також 2.
Так що ж це коїться людоньки? Справа у стандартах. Доречі у 2 версії пайтон round робив це “математично”, але у 3 версії це змінили на метод Rounding half to even, цей метод також відомий як банківське округлення. Він описаний у стандарті IEEE 754 (Standard for Floating-Point Arithmetic) і працює так:
23.5 => 24, −23.5 => −24
24.5 => 24, -24.5 => -24
Цей спосіб згідно стандарту є дефолтним (принаймні у актуальній редакції на момент розробки 3 версії пайтон) 11 сторінка, параграф 4. Rounding:
if the two nearest representable values are near, the one with its least significant bit zero shall be delivered
Це значить, що коли число розташоване точно посередині між двома найближчими значеннями, обирається значення, у якого найменш значущий біт (LSB) дорівнює нулю. Іншими словами, воно округлює до найближчого парного числа. Тобто у випадку із 2.5:
1. Два найближчі цілі числа до 2.5 - це 2 і 3.
2. 2.5 знаходиться точно посередині між цими двома числами.
3. Згідно з правилом, ми перевіряємо LSB (найменш значущий біт) обох чисел 2 і 3.
3.1 Двійкове представлення 2 - це 10, де LSB дорівнює 0.
3.2 Двійкове представлення 3 - це 11, де LSB дорівнює 1.
4. Оскільки правило зазначає обирати число з LSB, який дорівнює нулю, 2.5 округлюється до 2.
Банківське округлення допомагає уникнути накопичення помилок, які можуть виникнути при систематичному округленні вгору або вниз і як наслідок допомагає досягти більшої точності в середньому по всіх значеннях. Його часто використовують у фінансах, статистиці та аналізі даних та у мовах програмування. Ось тут є гарне пояснення - Is "banker's rounding" really more numerically stable?
Цікавий факт: одним із перших його адаптував AppleScript, але після багатьох запитів "зробити округлення таким як я його вчив у школі" вони додали команду
round 2.5 rounding as taught in school що є валідною командою у Apple Script 😁Ще у мовах програмування також використовують Rounding half away from zero, тобто так як і вказано у назві - до першого числа дальшого від нуля. Це працює схоже на Rounding half to even але без винятку для парних чисел:
23.5 => 24, −23.5 => −24
24.5 => 25, -24.5 => -25
Rounding half up по іншому округлює числа зі знаком мінус:
23.5 => 24, −23.5 => −23
24.5 => 25, -24.5 => -24
Мені стало цікаво а як у інших, чи може пайтон тут один так вирішив виділитись. Ось що мені вдалось знайти - перелік мов по обраному дефолтному округленню:
Rounding half to even: Python3, .NET, C#, Haskell, R, Delphi
Rounding half away from zero: C, С++, Objective C, MATLAB, Swift, Perl, Go, Ruby, Rust, Python2
Rounding half up: PHP, JavaScript, Java
Тож що ми дізнались з цього тексту? По-перше це те що не всі слідують стандартам (і ще і дивуються). По-друге, що коли вас просять округлити число - завжди питайте яким методом. Ще мабуть розумним було б зберігати числа у тому виді у якому вони є, а округлювати лише тоді коли віддаєте ці дані у UI.
Ну і якщо вам раптом треба округлити число у пайтоні якимось іншим методом - ви завжди можете це зробити за допомогою стдліба
Лінки:
- Is "banker's rounding" really more numerically stable?
- How .NET's Math.Round has Nothing to do with Maths. And That's OK!
- Python 3.x rounding behavior
- IEEE Standard for Binary Floating-Point Arithmetic