gallu’s blog

エンジニアでゲーマーで講師で占い師なおいちゃんのブログです。

PHPUnitの、小数点同士の比較

PHPUnitの本体記事は今度書く予定ですが、今日はちょろっと、横道を。
おいちゃん、2a問題( http://d.hatena.ne.jp/gallu/20061108/p1 )があったりあったりするので。
PHPUnitでは、まずもってassertEqualsを使うためしはなく、必ずと言っていいほど(っていうか必ず)assertSameを使うのですが。


ふと、本当にふと、微妙疑問に思って「まぁNGになるだろう」と思ったコードでNGを返してこず、些か「……はて?」となりました。

    $this->assertSame(0.1 + 0.2, 0.3);

あれれ???
念の為、確認。

    $this->assertSame(true, (0.1 + 0.2) === 0.3);

うんこっちはちゃんとNGになる。
……なんざましょ? って思って、コード潜ってみました、という備忘録。


とりあえず、Sameメソッド。

    public static function assertSame($expected, $actual, $message = '')
    {
        if (is_bool($expected) && is_bool($actual)) {
            static::assertEquals($expected, $actual, $message);
        } else {
            $constraint = new PHPUnit_Framework_Constraint_IsIdentical(
                $expected
            );

            static::assertThat($actual, $constraint, $message);
        }
    }

ifん中は今回は通らないので、else句をチェック。
まず、PHPUnit_Framework_Constraint_IsIdenticalは

    public function __construct($value)
    {
        parent::__construct();
        $this->value = $value;
    }

なので、単純に「値を保持している」だけのクラスぽい。…存在意義は不明(コードあちこち広域に読んだらわかるんだろうけど)。
で、次の行のassertThatが

    public static function assertThat($value, PHPUnit_Framework_Constraint $constraint, $message = '')
    {
        self::$count += count($constraint);

        $constraint->evaluate($value, $message);
    }

って感じ。
多分self::$countは「テストした数のカウント」だと予想しているので(コード未チェック)、その行はすっとばし。
$constraint->evaluateが、基本的には

        } else {
            $success = $this->value === $other;
        }

        if ($returnResult) {
            return $success;
        }

ってなってるので、「型厳密な、===による比較」が一端行われてるんだなぁ、というのは、理解。
ただ、その手前で

        if (is_double($this->value) && is_double($other) &&
            !is_infinite($this->value) && !is_infinite($other) &&
            !is_nan($this->value) && !is_nan($other)) {
            $success = abs($this->value - $other) < self::EPSILON;

ってのがあるので&self::EPSILONが

    const EPSILON = 0.0000000001;

なので。
「小数点の比較の場合は、誤差を割合と丁寧にチェックしてる」事が判明。


ははぁ面白い事やってるなぁ、と。
まぁ「双方が浮動小数点数の時だけ誤差チェック」なので、Sameに対する信頼はゆるぎないものとして相変わらず君臨しているので、腑に落ちた感じで調査終了。


余談。
assertEqualsメソッドは「なんかごちゃごちゃしてた」し使うつもりも基本まったくないので、まともに読んでませんw