6
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

golang の big.Float で誤差なし計算

Last updated at Posted at 2020-11-06

はじめに

golang で ETH の四則演算をする場合に、小数点以下の数字が18桁と非常に大きな浮動小数点計算が必用になります。
オーバーフローを意識しないと計算に誤差が出ますので、倍精度浮動小数点型を正しい用法で使用しましょう。

やってみよう

必用なもの

golangだけ

どんな誤差が出る?

1000ETHの予算からETHを減算後に、再加算します。
誤差がなければ、1000ETHに戻るはずです。

error.go
package main

import (
    "fmt"
    "math/big"
    "strings"
)

func main() {
    budget, _ := new(big.Float).SetString("1000")
    amount, _ := new(big.Float).SetString("0.123456789012345678")
    balance := new(big.Float).Sub(budget, amount)
    summary := new(big.Float).SetPrec(128).Add(balance, amount)

    fmt.Printf("budget  = %v\n", formatFloatPrice(budget, 18))
    fmt.Printf("amount  = %v\n\n", formatFloatPrice(amount, 18))

    fmt.Printf("balance = budget - amount = %v\n", formatFloatPrice(balance, 18))
    fmt.Printf("budget = balance + amount = %v\n", formatFloatPrice(summary, 18))
}

func formatFloatPrice(amount *big.Float, decimals int) string {
    str := amount.Text('f', decimals)
    if strings.Contains(str, ".") {
        str = strings.TrimRight(str, "0")
        if strings.HasSuffix(str, ".") {
            str = str + "0"
        }
    }
    return str
}

実行すると、1000ETHとなるはずが誤差のせいでうまくいきません。

実行結果
budget  = 1000.0
amount  = 0.123456789012345678

balance = budget - amount = 999.876543210987654309
budget = balance + amount = 999.999999999999999987

誤差をなくすには?

魔法のメソッド SetPrec() を使って浮動小数の精度を良くします。
※ ETC20トークンの場合は発行者が桁数を任意に指定できるため、下記の精度でもオーバーフローする可能性があります。

ok.go
package main

import (
    "fmt"
    "math/big"
    "strings"
)

func main() {
    budget, _ := new(big.Float).SetPrec(128).SetString("1000")
    amount, _ := new(big.Float).SetPrec(128).SetString("0.123456789012345678")
    balance := new(big.Float).SetPrec(128).Sub(budget, amount)

    summary := new(big.Float).SetPrec(128).Add(balance, amount)

    fmt.Printf("budget  = %v\n", formatFloatPrice(budget, 18))
    fmt.Printf("amount  = %v\n\n", formatFloatPrice(amount, 18))

    fmt.Printf("balance = budget - amount = %v\n", formatFloatPrice(balance, 18))
    fmt.Printf("budget = balance + amount = %v\n", formatFloatPrice(summary, 18))
}

func formatFloatPrice(amount *big.Float, decimals int) string {
    str := amount.Text('f', decimals)
    if strings.Contains(str, ".") {
        str = strings.TrimRight(str, "0")
        if strings.HasSuffix(str, ".") {
            str = str + "0"
        }
    }
    return str
}

無事1000ETHに戻りました!

実行結果
budget  = 1000.0
amount  = 0.123456789012345678

balance = budget - amount = 999.876543210987654322
budget = balance + amount = 1000.0
6
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?