Goで通貨を浮動小数点から整数に変換する最良の方法は何ですか?
------------- 質問の説明を追加 ----------
私の質問を少し拡張するために、以下は、0.004 の丸め値 (2 桁の通貨の場合) を追加することによって解決される「問題」として私が見ているものの例です。
私の知る限り、外部値はたとえば次のように保存されます。RDBMS の decimal、double、currency は、float として Go に「インポート」する必要があります。計算を実行するには、整数に変換するか、少なくともそれが 1 つの方法です。
以下の例では、fVal1 はデータベースからインポートされた量をエミュレートします。それに対して計算を実行するには、整数に変換したいと思います。これを行う最も簡単な方法は、図のように丸め数値を追加することです。
コード例:
var fVal1 float64 = 19.08
var f100 float64 = 100.0
var fRound float64 = 0.004
var fResult float64 = fVal1 * f100
fmt.Printf("%f * %f as float = %f\n", fVal1, f100, fResult)
var iResult1 int64 = int64(fResult)
fmt.Printf("as Integer unrounded = %d\n", iResult1)
var iResult2 int64 = int64(fResult + fRound)
fmt.Printf("as Integer rounded = %d\n", iResult2)
コンソール出力:
19.080000 * 100.000000 as float = 1908.000000
as Integer unrounded = 1907
as Integer rounded = 1908
----------- 質問への追記終わり -----------
Go で複数の通貨を処理する小さなパッケージを実装しました。基本的には、次のコード (以下) を中心に展開します。
浮動小数点数と整数の間の変換で丸め誤差があることを発見したとき、最初に試した丸め係数は 0.004 (小数点以下 2 桁) で、問題なく動作するように見えました。
基本的に、float から int への通貨変換のための次のコードは、これら 2 つの代替コード行を中心に展開します。
var iCcyAmt1 int64 = int64(fCcyBase * fScale)
var iCcyAmt2 int64 = int64((fCcyBase + fRound) * fScale)
2 番目の選択肢の「fRound」で使用されている丸めは「0.004」です。
このテスト プログラム (1000 万回の反復) を実行すると、「0.004」が追加されていない場合、一貫して約 588,000 セントの丸め誤差が発生し、丸め数値を使用した差はゼロになります。
これはこの状況を処理する安全な方法ですか (文字列を使用したくありません)、またはより良い方法はありますか?
テストプログラム:
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
var (
fRound float64 = 0.004
fScale float64 = 100.0
iCcyTotBase int64
iCcyTot1 int64
iCcyTot2 int64
)
const I_ITERS int = 10000000
rand.Seed(time.Now().UTC().UnixNano())
fmt.Printf("\nTesting Float to Integer (%d iterations)"+
" .........", I_ITERS)
for i := 0; i < I_ITERS; i++ {
var iCcyBase int64 = int64(999 + rand.Intn(9999999))
var fCcyBase float64 = float64(iCcyBase) / fScale
var iCcyAmt1 int64 = int64(fCcyBase * fScale)
var iCcyAmt2 int64 = int64((fCcyBase + fRound) * fScale)
iCcyTotBase += iCcyBase
iCcyTot1 += iCcyAmt1
iCcyTot2 += iCcyAmt2
}
var iCcyDiff1 int64 = iCcyTot1 - iCcyTotBase
var iCcyDiff2 int64 = iCcyTot2 - iCcyTotBase
fmt.Printf("\nDiff without rounding = %d\n", iCcyDiff1)
fmt.Printf("Diff with rounding = %d\n", iCcyDiff2)
}