読者です 読者をやめる 読者になる 読者になる

小数の値が等しいかどうかassertするJavaScriptテストコード

JavaScript

JavaScriptコードのテストを書いてて、小数の値が正しいかどうかテストしたくなった。でも 1/3 とか無限小数になるので、期待値をどう書けばいいかわからない。

なので、多少の誤差はあってもいいので、小数の値がだいたい等しいかどうか確認するコードを書いた。こんな感じ。

// ぴったり等しいことを確認する
function assertEqual(actual, expected) {
    var msg = "is" + ((actual === expected) ? " " : " NOT ") + "equal to";
    print(actual, msg, expected, "in ===");
}

// ちょっとぐらいの誤差は許して等しいことを確認する
function assertEqualFloat(actual, expected) {
    var equalFloat = function(actual, expected, accuracy) {
        // 期待した値に対する許容誤差の範囲
        if (!accuracy) {
            // デフォルト値として +-1% の誤差は許容する
            accuracy = 0.01;
        }

        var upperLimit, lowerLimit;
        if (expected >= 0) {
            upperLimit = expected * (1 + accuracy);
            lowerLimit = expected * (1 - accuracy);
        } else {
            upperLimit = expected * (1 - accuracy);
            lowerLimit = expected * (1 + accuracy);
        }

        return actual <= upperLimit && actual >= lowerLimit;
    };

    var msg = "is" + (equalFloat(actual, expected) ? " " : " NOT ") + "equal to";
    print(actual, msg, expected, "in equalFloat");
}

print ってのはRhinoの関数で、その名の通り文字列として出力する。

で、こいつを使ってテストしてみた。

var actualNums = [10/3, -(10/3), 0.0001/3, 0];
var expectedNums = [3.3333, -3.3333, 0.00003333, 0];

for (var i = 0; i < actualNums.length; i++) {
    assertEqual(actualNums[i], expectedNums[i]);
    assertEqualFloat(actualNums[i], expectedNums[i]);
    print("");
}

結果は以下。

% rhino equalFloat.js
3.3333333333333335 is NOT equal to 3.3333 in ===
3.3333333333333335 is equal to 3.3333 in equalFloat

-3.3333333333333335 is NOT equal to -3.3333 in ===
-3.3333333333333335 is equal to -3.3333 in equalFloat

0.000033333333333333335 is NOT equal to 0.00003333 in ===
0.000033333333333333335 is equal to 0.00003333 in equalFloat

0 is equal to 0 in ===
0 is equal to 0 in equalFloat

うん、いい感じ。

ちなみにこいつはrhinouniteqFloat 関数を参考にした(rhinounitのAPI一覧)。でもそれにはバグがあって負の数の時はうまく動かなかったので、ちょっと直した。

動作はRhinoで確認したけど、print 以外は普通のブラウザでも動くと思うよ。