round 函式在不同語言中的實現亂象
以前曾經寫過一篇小文四捨五入? 並不準確,介紹了 rounding 功能在遇到類似 2.5, 3.5 等小數部分恰好是半數的時候, 並不遵循我們更熟悉的“四捨五入”,而是採用“奇進偶舍”,或者叫做“四捨六入五成雙”的規則,英文叫做 Banker's rounding.
在那篇文章中,我以 Python 為例,round(2.5) 取值為 2, 而 round(3.5) 取值為 4. 而最近我才發現,這裡我寫得不對,各種語言的實現很亂,而 Python 2 和 3 竟然是不一致的.
對於 rounding half 的規則,有很多種實現方式:
-
round half up:向正無窮大進位,round(2.5) 為 3, 而 round(-2.5) 為 -2, Java 的 Math.round 採用這個規定
-
round half down: 向負無窮大進位
-
round half towards zero: 向 0 進位, round(2.5) 為 2, 而 round(-2.5) 為 -2
-
round half away from zero: 遠離 0 進位, C 語言的庫函式 round 是此規則, Python 2 和 JavaScript 等多種語言也採用的是這一規則.
-
round half to even: 向偶數進位,即之前介紹過的 Banker's rounding, 在統計學上有更好的效果,浮點數的標準 IEEE 754 預設採用此規則,Python 3 也選擇了它作為新規則
-
round half to odd: 和上面相反,向奇數進位,在計算機中幾乎沒有被用到
-
隨機 round: 統計學中更實用,但程式語言的實現不可能採用此規則
還有的語言提供多種實現, 比如 Ruby 預設用的是 round half away from zero, 但也提供了 :even 和 :odd 的選項。
簡直太亂了,和對負數的取模運算有的一拼。我個人猜測可能是由於很多語言沿襲了 C 語言的規則,而 IEEE 754 作為浮點數的標準,又比 C 語言誕生得晚。 這兩種成為了最廣泛採用的規則, 至於為什麼 Java 選擇了不走尋常路,就不得而知了。
C 語言後來也提供了相關的巨集定義,以支援不同規則,但庫函式的 round 一直保持定義不變。