はじめに
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