0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

DENSOAdvent Calendar 2023

Day 13

JavaScriptで数字に単位(桁)を付ける[WIP]

Last updated at Posted at 2023-12-13

はじめに

この記事では、JavaScriptを使用して数値に桁の単位(例:万、億)を付ける方法について紹介します。業務で数値に桁を付ける必要が生じた際に、大きな数値を扱うことの課題に直面しました。この経験から、数値に桁をつける際の注意点を共有しようと思います。

やりたいこと

目的は、入力された数字に対して、桁でフォーマットした文字列を返すことです。業務では、10垓を超えるような大きな値を扱う可能性があるので、100垓以下の値であれば正確な桁付き数字を返すことができることを目指します。
例:111111111 -> 1億1111万1111
  100000000 -> 1億

「垓」ってなんやって方は以下の記事などをご参照ください。訳がわからない単位が大量に紹介されています。

実装

以下の関数は、数値を桁ごとに分け、適切な単位を付けることでこの要件を満たします。具体的には、数値を文字列に変換し、それを逆順にして4桁ごとに区切り、各セグメントに対応する単位を付け加えます。
業務では扱わない予定ですが、負の数や少数にも対応できるようにしました。

const UNITS = ['', '', '', '', '', ''];

function formatNumberWithUnits(num) {
    const toIntReverse = str => parseInt(str.split("").reverse().join(""));

    let isNegative = num < 0;
    let absNumber = Math.abs(num);
    let reversedStr = Math.floor(absNumber).toString().split('').reverse().join('');
    let splitNumbers = reversedStr.match(/.{1,4}/g).map(toIntReverse);

    let formattedArray = splitNumbers.map((num, i) => num === 0 ? null : num + UNITS[i])
                                     .filter(numWithUnit => numWithUnit !== null)
                                     .reverse();

    let formattedNumber = formattedArray.join('');
    return isNegative ? '-' + formattedNumber : formattedNumber;
}

実行結果

// 入力例1
const num1 = 1_0000_1111;
console.log(formatNumberWithUnits(num1)); // 出力: 1億1111

// 入力例2
num = 1111_2222_3333_4443
console.log(formatNumberWithUnits(num));  // 出力: 1111兆2222億3333万4443

// 入力例3
num = 1_2222_3333_4444_5555
console.log(formatNumberWithUnits(num)); // 出力: 1京2222兆3333億4444万5556

// 入力例4
num = 1111_2222_3333_4444_5555
console.log(formatNumberWithUnits(num)); // 出力: 1111京2222兆3333億4444万6000

// 入力例5
const num3 = 11_1111_1111_1111_1111_1111;
console.log(formatNumberWithUnits(num3)); // 出力: 1垓1111京1111兆1111億1111万NaN

1京以下程度の入力に対しては、期待通りに動作しました。しかし、1京を超える数値の場合、誤差が発生したり、10垓を超える数値の場合、指数表記に変わり、NaNが出力される問題が発生しました。これらを解決するため、まずはNumber型について調査しました。

Numberについて

  • Numberの概要
    Number型はJavaScriptで数値(小数点数 and 整数)を表現するために使用されます。内部的にはIEEE 754 の倍精度 64ビットバイナリー形式で数値が表現されています。

  • 表せる範囲と計算精度
    計算精度によって表せる範囲が変わるようで、整数に対して計算精度が保証される範囲は -253+1 ~ 253-1 です。
    計算精度が保証されている値の最小値と最大値を取得するソッドも存在します。
    単位を付けた数字の範囲は以下とおりです。

min_safe = Number.MIN_SAFE_INTEGER
max_safe = Number.MAX_SAFE_INTEGER
console.log(`${formatNumberWithUnits(min_safe)} ~ ${formatNumberWithUnits(max_safe)}`)
// 出力: -9007兆1992億5474万991 ~ 9007兆1992億5474万991

精度が損なわれますが、上記の範囲を超えた値も表現できるようです。(すごい)
Numberで表現可能な範囲は以下の通りです。

min = Number.MIN_VALUE
max = Number.MAX_VALUE
// 指数表記のため今回のメソッドは使えず
console.log(`${min} ~ ${max}`)
// 出力: 5e-324 ~ 1.7976931348623157e+308

上記の範囲外はInfinityになるようです。

uhyoさんの記事で、Numberについて詳しく説明されているので正確な情報を知りたい方はご覧ください。

以上より、10垓を超えるような値を正確に表現するには、Number型では不十分であることがわかりました。
そこで、より大きな値を正確に扱えるDecimalを使うことを検討します。

参考:https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Number

Decimalについて

decimal.jsを使ってより大きな値を扱えるようにならないか調査します。

  • 大きな数値の扱い方
  • DecimalBigIntの利点

まとめ

この記事では、JavaScriptで数字に桁の単位を付ける方法について検討しました。指数表記にならない範囲の数値であれば問題なく表示できました。今後、指数表記への対応や、外部ライブラリを使用してより大きな数値を正確に表現する方法についてまとめ、この記事を更新していく予定です。

0
2
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?