MongoDB上でNumberDecimal()を使いdecimalで小数を格納していたのだが、それを"mongo-go-driver"を介してDecode()する時に、cannot decode 128-bit decimal into a float32 or float64 typeとエラーが出て困ったので、各々の小数周りの仕様を調べた。
それぞれの小数周りの仕様へのリンク集としても役立つはず。
Go
Go上で小数に対応する型は、Numeric typesの仕様によると、float32とfloat64のよう。
Protocol Buffers
"proto3"のLanguage GuideにあるScalar Values Type表によると、proto上での小数表記はfloatとdooubleで、それぞれGo上ではfloat => float32、double => float64となるらしい。
MongoDB
MongoDB上では、schema定義のドキュメント曰く、doubleとdecimalが存在する。これはBson Typeにあたるので、Bsonの仕様書を見てみると、どうやら64bitで表現されるのがdoubleであり、128bitで表現されるのがdecimal (decimal128)であることがわかる。
MongoDBとGoの間でやり取りするには、基本的に"mongo-go-driver"を使うことになると思うが、その中の"go.mongodb.org/mongo-driver/bson"についての箇所を見ると、BSONのdoubleはfloat64に、128-bit decimalはprimitive.Decimal128にエンコードされることがわかる。
"mongo-go-driver"でのDecimal128の扱いを読んでみると、BigInt()でbig.Intとexponential部分を返すメソッドはあるようだが、Decimal128をGo標準のfloat64などに変換するメソッドはないことがわかる。
結論 string 最強
今回は、計算したい要件がなく、ただMongoDBから取得したデータをGoを介してProtocol Buffer経由でクライアントに送りたいだけだったので、結局stringにすることにした。他にも、別にMongoDBに入れるのにdecimalであることにこだわりがなければ、doubleを使用することでGo上ではfloat64として扱うことができるので、問題は発生しない。
もし計算したいなら、Decimal128をstringにして、それをさらにfloat64などにするのが良いのか、BigInt()を使って頑張れるのか("math/big"のFloatに頑張って変換できれば良いのだが)、Decimal128のまま計算する方法があるのかはわかっていない。
何れにしても、小数周りはどの言語でも辛いんだなぁと思った。
(jsだけじゃなかった。。。