JavaScript で 有効数字 28桁の Decimal 型を書いた

JavaScript の Decimal 型を書いて、GitHub と npm で公開した。

https://github.com/hiroshi-manabe/JSDecimal
https://npmjs.org/package/jsdecimal


C# (っていうか .NET)には Decimal型 というのがあるらしい。

十進数で 28〜29桁(なんだその「28〜29桁」ってのは…後述)の精度を保証するとのこと。

それで、お金の絡む計算にはよく使われるそうだ。


で、人から聞いた話だけど、そういう計算をサーバ側でしていたのをクライアント側でもしたいということがあるようだ。

Ajax でも使って、サーバ側で計算させたものを取ってくればいいと思うのだが、そうもいかないこともあるらしい。


何に使うかというと、ちゃんとした計算はサーバでやるものの、プレビューみたいなものをユーザに見せたいとかいうときとか。

プレビューなら、「※計算結果は実際のものと1円程度の誤差がある場合があります」とでも書いておけばいいじゃないかと思うんだけど。

どうもそうもいかなかったりするらしい。

(プレビューの結果が 1円違ったからといって、それが何だっていうんだ)


まあ、それでローカルでも C# と結果がほぼ必ず同じになるコードを書くとなったら、それは別に難しいことじゃない。

浮動小数点の計算がどういうものか、内部でどういうふうに実装されているか、表せる範囲はどのようなものかなど、そういったことをわかっている普通のプログラマなら、十進数の小数による誤差が出ないように、適切に 10 の何乗を掛けたり割ったりしたらうまくつじつまを合わせられるところだと思う。


それが、有名な FizzBuzz 問題でもわかるように、そういう意味の普通のプログラマは多くない。

FizzBuzz が書けるレベルの普通のプログラマが 2人に 1人だとすると、浮動小数点数をわかっている普通のプログラマは 20人に 1人ぐらいかもしれない。

そういうわけで、適当につじつまを合わせるというのも難しい。


それで適当に既存のライブラリを探したり、独自クラスを書いたりすることになる。

しかし、JavaScript の十進数ライブラリの実装は C#(.NET)とは違うのが普通だ。まあ、浮動小数点をわかっている普通のプログラマが使うならいいんだけど以下略。

また独自クラスを書くにしても中途半端なものになり、また金融関係のところで書かれたものが公開されるわけもなく、何度も車輪の再発明が繰り返される。

その過程で膨大な人間の膨大な時間が無駄になる。

悲しいことだ。


そこで、自分で C#(.NET)の decimal に近いものを作ったらいいんじゃないかと思った。

ECMA の C# の仕様 (PDF)によると、十進28桁の有効数字が表せればいいらしい。

ということは、4つの Number に分解して、それぞれに 10進 7桁分を持たせるようにすることが考えられる。

そうすると、2個で 10進 14桁と JavaScript で整数として扱える範囲なので、2個分同士の演算が自由に行えることになる。


その路線で書いたのが、今回書いた Decimal 型

JavaScript でのモジュールの作り方とかを知らないので見よう見まねだけど。


使い方は、ブラウザなら lib/decimal.js を読み込んで、次のような感じで。

var result = Decimal(0).add(0.1).mul(0.2).sub(0.3).div(0.4).toNumber();   // 0.7


残念なのは、実際の C# の仕様と厳密には一致しないということ。

ECMA の仕様によると 28桁「以上」の有効数字ということになっているが、MicroSoft の仕様では仮数部は「2^96 で表せる範囲」で、それを 10の何乗かでスケールしたものだそうだ。

このせいで、たとえば 7.92... という数字は有効数字が 29桁なのに、7.93... という数字は有効数字が28桁という仕様になっている。

なんで 10進数を扱う型で 2進数を意識しなくちゃいけないんだクソが。


こちらでも .NET の仕様に合わせて、仮数部が 0〜2^96-1 という実装をすることも考えたけれど、2^96 同士の演算が簡単にできない状況で仮数部を 2進数で持つというのは、10進の数字との変換がやっかいになるだけで、何もいいことがない。

10進基準で有効数字が 28桁になったり 29桁になったりするのもアホすぎる。

だから、結局仮数部は 10進数7桁までの数字×4 として持つことにした。


これで少しは世の中の不幸が減らせるんだろうか?

まあ、信頼性の問題や外部コードを使う問題、ライセンス(MIT だけどそれでも)の問題、それに C#(.NET)との微妙な非互換性とかで、使われることは多くないかもしれない。


それに、もし仮に誰かに使われて不幸を減らせたとしても、自分に返ってくることはない。

結局のところ、就職するにしても求められるのは「○○という技術に詳しいか」といったところで、浮動小数点の計算の詳しいところを知っているかどうかなんてどうでもいいことだ。

クソ。このコード使って金融関係のコード書くやつがいたら、そこの有り余ってる金分けてくれ。


やっぱり「人のため」なんて思ってタダでコードを書くのはダメだな。

こんなちっぽけなモジュールでも損した気分になる。

まあ、もう書いてしまったし、どうせ換金方法もないし、ほかに使い道もないから公開しておく。