なぜ「安全な数値計算」が業務で重要になるのか
業務コードの数値計算は、単なる足し算・引き算では終わりません。
外部 API のレスポンス、フォーム入力、DB の値など、「本当に数値か分からないもの」を相手に計算します。
そこでよく起きるのが次のような事故です。
"10" と数値を足しておかしな結果になるNaN が一度混ざって、以降の計算結果が全部 NaN になる
0 で割って Infinity が出て、そのまま画面に表示されるnull や undefined をそのまま計算に使ってしまう
「安全な数値計算」というのは、こうした事故を防ぐために
計算の前後で「値が本当に“まともな数値”かどうか」をチェックしながら計算する、という考え方です。
JavaScript の数値まわりの落とし穴を整理する
number 型なのに「壊れている値」がある
JavaScript の number 型には、次のような特殊値があります。
NaNInfinity-Infinity
そして、これらはすべて typeof value === "number" で true になります。
つまり、「number だから安全」とは全く言えません。
一度 NaN が混ざると、以降の計算はほぼ全部 NaN になります。
const a = Number("abc"); // NaN
const b = a + 10; // NaN
const c = b * 2; // NaN
JavaScriptInfinity も同様で、0 で割ったりすると簡単に出てきます。
const x = 10 / 0; // Infinity
JavaScriptこれらを「安全な数値」と同じ扱いで計算に使うと、業務ロジックが壊れます。
安全な数値判定ユーティリティをまず用意する
有限の数値かどうかを判定する isFiniteNumber
安全な数値計算の土台は、「この値は計算に使っていいか?」を判定する関数です。
function isFiniteNumber(value) {
return typeof value === "number" && Number.isFinite(value);
}
JavaScriptこの関数は、次のように振る舞います。
isFiniteNumber(10); // true
isFiniteNumber(-3.14); // true
isFiniteNumber(NaN); // false
isFiniteNumber(Infinity); // false
isFiniteNumber("10"); // false
JavaScriptここでの重要ポイントは二つです。
「型が number であること」を確認している
「Number.isFinite で NaN / Infinity を除外している」
これで、「本当に“まともな数値”だけを通すフィルター」ができます。
文字列も含めて「数値として安全か」を判定する
入力や API レスポンス向けの isFiniteNumeric
実務では、数値が文字列で来ることが多いので、number だけでなく string も受け付ける判定関数があると便利です。
function isFiniteNumeric(value) {
if (typeof value === "number") {
return Number.isFinite(value);
}
if (typeof value === "string") {
if (value.trim() === "") return false;
const n = Number(value);
return Number.isFinite(n);
}
return false;
}
JavaScript動きはこんな感じです。
isFiniteNumeric(10); // true
isFiniteNumeric("10"); // true
isFiniteNumeric("10.5"); // true
isFiniteNumeric("abc"); // false
isFiniteNumeric(""); // false
isFiniteNumeric("Infinity"); // false
isFiniteNumeric(Infinity); // false
JavaScriptここでの深掘りポイントは、「空文字や変換不能な文字列をきちんと弾いている」ことです。
これを通過した値だけを計算に使う、というルールにすると、数値計算がかなり安全になります。
安全な四則演算ユーティリティを作る
足し算:safeAdd
まずは足し算の安全版を作ってみます。
function safeAdd(a, b, defaultValue = 0) {
if (!isFiniteNumeric(a) || !isFiniteNumeric(b)) {
return defaultValue;
}
const x = Number(a);
const y = Number(b);
const result = x + y;
return Number.isFinite(result) ? result : defaultValue;
}
JavaScript使い方の例です。
safeAdd(10, 5); // 15
safeAdd("10", "5"); // 15
safeAdd("10", "abc"); // 0(defaultValue)
safeAdd(10, Infinity); // 0(defaultValue)
JavaScriptここでやっていることは、
計算前に「両方が有限数として解釈できるか」をチェック
数値に変換してから計算
結果が Infinity などになっていないかを再チェック
という三段構えです。
割り算:safeDivide
割り算は特に危険なので、専用の安全版を用意する価値があります。
function safeDivide(a, b, defaultValue = 0) {
if (!isFiniteNumeric(a) || !isFiniteNumeric(b)) {
return defaultValue;
}
const x = Number(a);
const y = Number(b);
if (y === 0) {
return defaultValue;
}
const result = x / y;
return Number.isFinite(result) ? result : defaultValue;
}
JavaScript使い方です。
safeDivide(10, 2); // 5
safeDivide("10", "2"); // 5
safeDivide(10, 0); // 0(defaultValue)
safeDivide("abc", 2); // 0(defaultValue)
JavaScriptここでの重要ポイントは、「0 で割る前にチェックしている」ことです。
これにより、Infinity をそもそも発生させない設計になっています。
計算結果の「範囲」までチェックする
業務ルールを組み込んだ安全計算
実務では、「数値として正しいか」だけでなく、
「業務的にあり得る範囲か」も重要になります。
例えば、「割引率は 0〜100 の間だけ許可したい」というケースなら、
次のようなユーティリティが考えられます。
function safePercentage(value, defaultValue = 0) {
if (!isFiniteNumeric(value)) return defaultValue;
const n = Number(value);
if (n < 0 || n > 100) return defaultValue;
return n;
}
JavaScript使い方です。
safePercentage("10"); // 10
safePercentage(150); // 0(defaultValue)
safePercentage("abc"); // 0
JavaScript「安全な数値計算」は、
単に NaN や Infinity を避けるだけでなく、
こうした「業務ルールを満たす数値だけを通す」ことまで含めて設計すると、かなり強くなります。
実務での具体的な利用イメージ
API レスポンスから金額を扱う
外部 API から金額が文字列で返ってくるケースを考えます。
const response = {
amount: "1000",
taxRate: "10",
};
JavaScriptこれをそのまま計算に使うのではなく、安全な数値変換+計算を挟みます。
function calcTotalAmount(amount, taxRate) {
if (!isFiniteNumeric(amount) || !isFiniteNumeric(taxRate)) {
return 0;
}
const a = Number(amount);
const r = Number(taxRate);
const tax = safeMultiply(a, r / 100);
return safeAdd(a, tax);
}
JavaScriptsafeMultiply も safeAdd と同じパターンで作れます。
こうしておけば、「API が変な値を返してきても、合計金額が NaN になる」ことは避けられます。
フォーム入力からページング計算をする
ページ番号やページサイズをユーザー入力から受け取るケースも典型的です。
function normalizePaging(input) {
const page = isFiniteNumeric(input.page) ? Number(input.page) : 1;
const pageSize = isFiniteNumeric(input.pageSize) ? Number(input.pageSize) : 20;
const safePage = page < 1 ? 1 : page;
const safePageSize = pageSize <= 0 ? 20 : pageSize;
return { page: safePage, pageSize: safePageSize };
}
JavaScriptここでも、「数値として妥当か」「範囲として妥当か」を両方見ています。
これを通過した値だけを使って計算すれば、ページングまわりのバグはかなり減ります。
小さな練習で感覚をつかむ
次のような値の組み合わせを用意して、自分で isFiniteNumeric, safeAdd, safeDivide を実装して試してみてください。
10 と 5"10" と "5""10" と "abc"10 と 0Infinity と 1"Infinity" と "1"
それぞれについて、「素の演算子で計算した結果」と「安全版ユーティリティで計算した結果」を比べてみると、
「どこで NaN や Infinity が出るのか」「安全版がどう防いでいるのか」がはっきり見えてきます。
そこまで体感できたら、あとはあなたのプロジェクトの中で、
「ここ、たまに NaN が紛れ込んでバグるんだよな」
「この計算、0 で割る可能性あるよな」
という場所に、少しずつ「安全な数値計算ユーティリティ」を差し込んでいけば、
数値まわりのバグはかなり減っていきます。
