JavaScript
入門
浮動小数点

JavaScriptで小数点の誤差が発生する件の備忘録

プログラマになりたいフロントエンドの人です。
なんだかものすごく楽勝そうに見えてものすごく躓いたので備忘録に…。

どこからこの子は来たの?ってなった

消費税の計算をしてみたところ…

script.js
function totalPrice(price, tax=1.08){
  return price*tax;
}
document.write(totalPrice(450)+"円"); 

こうなった!

486.00000000000006

486円になるはずなのに、この小数点以下はどこから来たんでしょう…。
priceが300円だったら普通に324円になるのに、450円だったら現れるこの小数点以下の子達は一体どこから来たんでしょう…。

丸め誤差

そもそも、コンピューター内部で数値は2進法で計算されます。

10進法 → 0・1・2・3・4・5
2進法 → 0・1・10・11・100・101

ので、桁数の多い数字や小数点を処理するのは大変…というかできないんです(´・ω・`)ショボン

そこで、うまく丸こんだ(丸め操作 ≒ 切り上げ・切り捨てなど)時に発生する誤差のことを丸め誤差と言います。

IEEE754

うまく丸めこむために、具体的には、浮動小数点数が使われます。
お馴染みの固定小数点数は小数点の位置を境目に0より小さい数字であると表現しているのですが、浮動小数点数は、データ(ビット)で数値を表現します。
その浮動小数点数の標準がIEEE754(浮動小数点数算術標準)なんです。

オフィシャルサイトわかりづらすぎてwikipediaさんがありがたい。

データとして数値を扱うためのルールが細かく規定されています。
やっと話が見えて来ました!

…で?どうすれば?

javascriptで計算しない、という選択肢を除いて、一体どうすればいいのでしょうかと思いまして、やり方を探してみました。

全部整数にしてみる

function totalPrice(price, tax=108, taxInteger=100){
  return price*tax/taxInteger;
}
document.write(totalPrice(450)+"円"); 

=> 486円

1.08*100だと結局丸め操作入るからな〜と思い、この書き方で求めている解は出るようになりましたが、実際いちいち手作業で100倍にして100で割るのは現実的ではないので他の方法はないものかと思い…

ライブラリを使う

ちょっとした計算のためだけになぁという感じもしますが、今のところ現実的な解決策です。
ここらがメジャーなライブラリのようです。

BigDecimal.js
decimal.js

ライブラリ使わずに自前でガシガシ書けるようになりたい!

参考になったサイト

わかりやすかったです。ありがとうございます!

ダックさん - ビットで表す数字の世界~浮動小数点編~
Asial Blogさん - JavaScriptによる小数計算の誤差を無くす