Elementのvalueプロパティの型とJSでの演算の誤差

投稿者: | 2021年12月16日

実験で使う計算機を作って遊んでいましたが、不具合が見つかりまくったので、その修正の過程で調べたことをまとめています。

不具合は主に以下の2つです。

  • getElementById()で取得したElementのvalueプロパティの型によるもの
  • JavaScriptにおける小数点の計算の誤差によるもの

1. getElementById()で取得したElementのvalueプロパティ

getElementById()メソッドはElementオブジェクトを返します。問題になったのは、Elementオブジェクトのvalueプロパティの型はなんだろうということです。

答えはHTMLInputElementにあり、valueプロパティはString型になります。

この値を変数に渡すのですが、inputで入力した値が整数ならば数値型と推論され、小数ならば文字列型と推論されます。

JavaScriptの型推論に明るくないのでなんとも言えませんが、バグ回避のために、入力された値を数値として扱いたい場合はNumberを用いたり、小数ならばparseFloatを用いて明示的に型変換を行うべきだと思いました。

2. 小数点の計算誤差とその回避

有名な話ですが、JavaScriptでは0.1 + 0.2=0.30000000000000004となります。これは計算誤差によるものです。

よって、これを小数を一度文字列にすることで回避します。

次のページのアルゴリズムを参考にしています。JavaScriptでの小数点の計算の誤差について

以下のコードは加算の場合の例になります。

//小数の演算時の誤差を修正するために小数点の位置を探索するメソッド
//参考:https://qiita.com/k_moto/items/0b576a3351b77fb0aa98
function getDotPosition(value) {
//一度数値を文字列化
const strValue = String(value);
//小数点の位置の初期化
let dotPosition = 0;
//小数点の位置を取得(ない場合-1)
const whereDot = strValue.lastIndexOf('.');
//小数点が存在する時位置を取得
if (whereDot != -1) {
dotPosition = (strValue.length - 1) - whereDot;
}
//小数点の位置を返す
return dotPosition;
}
//加算(小数の計算の誤差を修正したもの)
function addValue(value1, value2) {
//それぞれの値の小数点の位置を取得
const dotPosition1 = getDotPosition(value1);
const dotPosition2 = getDotPosition(value2);
//小数点以下の位が大きい方の位置を取得
const maxPosition = Math.max(dotPosition1, dotPosition2);
//大きい方に小数の桁を合わせて文字列化し、小数点を除いて整数にする
const intValue1 = parseInt((value1.toFixed(maxPosition) + '').replace('.', ''));
const intValue2 = parseInt((value2.toFixed(maxPosition) + '').replace('.', ''));
//最後に割る値を計算
const div = Math.pow(10, maxPosition);
//整数値で足し算した後、10^Nで割る
return (intValue1 + intValue2) / div;
}

ライブラリを使う手や、うまいこと四捨五入を用いる方法などもあるようです。

競技プログラミングでよく使う、計算する値を予め整数にする方法はJavaScriptだとうまく行かないことがあるようです。

3. おわりに

久しぶりに競技プログラミングの問題を解いたらコードをバグらせまくって死にました。最近JavaScriptを書いたり、GASを書いたり、VBAを書いたりして遊びすぎたようです。ちゃんと精進します。

ところでJavaScriptの動的型付けが苦手なので、TypeScriptどうや?って言われたのですが、どうなんだろう?いい感じならTypeScriptで書き換えたいところ。

4. 参考文献

コメントを残す

This site uses Akismet to reduce spam. Learn how your comment data is processed.