SafeMath

程式語言精度計算/浮點數計算的問題早已不是都市傳說,問題出在某些浮點數無法準確地使用二進制表示,所造成的「錯誤」。但每次聽到或見到年輕軟體工程師因此踩坑,也還是會捏出一把冷汗,倘若是發生在對數字極度敏感的場景,是難以收復的。

常理來說,0.58 x 100 應為 58,但在某些程式語言裡,在不進行「特殊」處理下,答案會為 57 而非 58。

本文嘗試整理各程式語言遇到此類計算時的結果,而解法不外乎就是『使用支援高精度的函式來處理』,目前各程式語言都有對應的解法,此不詳述。

Java

class Demo {
    public static void main(String[] args) {
        System.out.print("0.56*100 => ");
        System.out.println((int)(0.56*100));
        System.out.print("0.57*100 => ");
        System.out.println((int)(0.57*100));
        System.out.print("0.58*100 => ");
        System.out.println((int)(0.58*100));
        System.out.print("0.59*100 => ");
        System.out.println((int)(0.59*100));
    }
}
$ javac Demo.java
$ java Demo
0.56*100 => 56
0.57*100 => 56
0.58*100 => 57
0.59*100 => 59

有 0.57*100 及 0.58*100 計算的「錯誤」。

Perl

$ perl -de1
DB<1> print int(0.56*100)
56
DB<2> print int(0.57*100)
56
DB<3> print int(0.58*100)
57
DB<4> print int(0.59*100)
59

有 0.57*100 及 0.58*100 計算的「錯誤」。

Python

$ python3
Python 3.6.9 (default, Jan 26 2021, 15:33:00)
[GCC 8.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> int(0.56*100)
56
>>> int(0.57*100)
56
>>> int(0.58*100)
57
>>> int(0.59*100)
59

有 0.57*100 及 0.58*100 計算的「錯誤」。

Ruby

$ irb
irb(main):001:0> RUBY_VERSION
=> "2.5.1"
irb(main):002:0> p (0.56*100).to_i
56
=> 56
irb(main):003:0> p (0.57*100).to_i
56
=> 56
irb(main):004:0> p (0.58*100).to_i
57
=> 57
irb(main):005:0> p (0.59*100).to_i
59
=> 59

有 0.57*100 及 0.58*100 計算的「錯誤」。

PHP

$ php -a
Interactive mode enabled
php > echo phpversion();
8.0.5
php > echo intval(0.56*100);
56
php > echo intval(0.57*100);
56
php > echo intval(0.58*100);
57
php > echo intval(0.59*100);
59

有 0.57*100 及 0.58*100 計算的「錯誤」。

JavaScript

$ node
> parseInt(0.56*100)
56
> parseInt(0.57*100)
56
> parseInt(0.58*100)
57
> parseInt(0.59*100)
59

有 0.57*100 及 0.58*100 計算的「錯誤」。

Go

package main
import "fmt"
func main() {
    fmt.Print("0.56*100 => ")
    fmt.Println(0.56*100)
    fmt.Print("0.57*100 => ")
    fmt.Println(0.57*100)
    fmt.Print("0.58*100 => ")
    fmt.Println(0.58*100)
    fmt.Print("0.59*100 => ")
    fmt.Println(0.59*100)
}
$ go run demo.go
0.56*100 => 56
0.57*100 => 57
0.58*100 => 58
0.59*100 => 59

沒有 0.57*100 及 0.58*100 計算的「錯誤」。

結論

這邊測試了七種程式語言,”java”, “perl”, “python”, “ruby”, “php”, “javascript”, “go”,發現僅有 Go 沒有 0.57*100 及 0.58*100 計算「錯誤」的問題。