【JavaScript】小数値の桁数を求める方法
更新日:2023/07/24
JavaScriptで小数値の桁数を取得する方法をお伝えします。
今回は数値型と数値形式の文字列の2種類に分けました。
数値文字列の場合
ブラウザで入力された数値は文字列で受け取ります。
また、外部から取得した数値データも文字列であることが多いです。
そのような数値文字列の小数部の桁数を求めるときは、次のようなコードで実現できます。
const getStringFractionalDigits = numberString =>{
// 数値文字列かどうかのチェック
if( typeof numberString !== "string" || isNaN( Number(numberString) )) return false;
// 小数部を取得
const f = numberString.trim().split(".")[1];
if( f === undefined || f.length === 0) return 0;
// 後方の"0"を取り除いた桁数取得
return f.length - ["1",...f].reverse().findIndex( e=>e!="0");
}
考え方は単純ですね。
最初の引数チェックは、お好みで。
必要なければ削除してください。
次に念のため、trim()で前後のスペースを削除します。
その後、小数点で文字列を分割します。
小数点で分割できない、または小数部の文字数が0のときは、桁数0になります。
次に、小数の後方に "0" が記述されていることを想定して、この部分を含めない文字数を返します。
方法はいくつか考えられますが、今回は後方から"0"以外の数値が出現する位置を求めて、元の文字列長から引いています。
実際には、配列をreverse()で反転して、先頭から検索しています。
また、["1",...f] の最初の "1" は、小数部が全て"0"だったときの対処をしています。
この関数を使って、数値文字列の小数桁数を求めてみます。
console.log( getStringFractionalDigits("1.2345") ); // 結果: 4
console.log( getStringFractionalDigits("1.2300") ); // 結果: 2
console.log( getStringFractionalDigits("1.") ); // 結果: 0
console.log( getStringFractionalDigits(".234") ); // 結果: 3
console.log( getStringFractionalDigits("1.0000") ); // 結果: 0
上手く取得できていますね。
コードの最後の行は、ECMAScript2023で導入されたfindLastIndex()を使用すると、次のように簡潔に記述できます。
return [...f].findLastIndex( e=>e!="0" ) + 1;
数値型の場合
数値型のデータから小数部の桁数を取得するコードは、次のようになります。
const getdecimalPrecision = (number,significant_digits=6) =>{
// 数値文字列かどうかのチェック
if( typeof number !== "number" ) return false;
const f = number.toFixed(significant_digits).split(".")[1];
if( f === undefined || f.length === 0) return 0;
console.log( f );
// 後方の"0"を取り除いた桁数取得
return f.length - ["1",...f].reverse().findIndex( e=>e!="0");
}
ほぼ、数値文字列のときと同じです。
異なるのは引数チェックと、数値をtoFixed(有効桁数)で文字列化している箇所だけです。
有効桁数を気にしないときは、toString()でもOKです。
とりあえず、テストしてみます。
console.log( getdecimalPrecision(1.2345) ); // 結果: 4
console.log( getdecimalPrecision(1.2300) ); // 結果: 2
console.log( getdecimalPrecision(1. ) ); // 結果: 0
console.log( getdecimalPrecision(.234) ); // 結果: 3
console.log( getdecimalPrecision(1.0000) ); // 結果: 0
よさそうですね。
ではなぜ最初に文字列化しているかというと、数値のままで計算するのが困難だからです。
最初に次のようなコードを考えました。
let digits = 0;
let num = number;
while( !Number.isInteger( num ) ){
digits++;
num *= 10;
}
return digits;
Number.isInteger()は、数値が整数かどうかを判定してくれます。
つまりこのコードは、数値を10倍していき、整数になるまでの回数をカウントしています。
このコードを初期値を 12.3456 として実行すると、結果が15になります。
期待した結果は4なので、大きくズレています。
原因を探るために、10倍後の値を確認してみると...
(1) 123.45599999999999
(2) 1234.56
(3) 12345.599999999999
(4) 123455.99999999999
(5) 1234559.9999999998
(6) 12345599.999999998
(7) 123455999.99999999
(8) 1234559999.9999998
(9) 12345599999.999998
(10) 123455999999.99998
(11) 1234559999999.9998
(12) 12345599999999.998
(13) 123455999999999.98
(14) 1234559999999999.8
(15) 12345599999999998
途中で誤差が出ていますね。
こりゃダメだ、ということで、最初に文字列化することにしました。
桁数が多いときは注意が必要
数値リテラルや数値文字列を数値型に変換するとき、整数部と小数部の合計桁数が多いほど、誤差が大きくなります。
console.log( (10.999).toFixed(32) ); // 10.99900000000000055422333389287814
console.log( (10000.999).toFixed(32) ); // 10000.99899999999979627318680286407471
console.log( (1000000.999).toFixed(32) ); // 1000000.99899999995250254869461059570313
console.log( (1000000000000.999).toFixed(32) ); // 1000000000000.99902343750000000000000000000000
console.log( (10000000000000.999).toFixed(32) ); // 10000000000000.99804687500000000000000000000000
console.log( (100000000000000.999).toFixed(32) ); // 100000000000001.00000000000000000000000000000000
最後以外は、有効桁数を3桁にして4桁目を四捨五入すればうまくいきそうです。
しかし、最後は小数が消えてしまいました。
リテラル値の場合は、コードでのチェックは難しいですね。
更新日:2023/07/24
関連記事
スポンサーリンク
記事の内容について
こんにちはけーちゃんです。
説明するのって難しいですね。
「なんか言ってることおかしくない?」
たぶん、こんなご意見あると思います。
裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。
そんなときは、ご意見もらえたら嬉しいです。
掲載コードについては事前に動作確認をしていますが、貼り付け後に体裁を整えるなどをした結果動作しないものになっていることがあります。
生暖かい視線でスルーするか、ご指摘ください。
ご意見、ご指摘はこちら。
https://note.affi-sapo-sv.com/info.php
このサイトは、リンクフリーです。大歓迎です。