Scalaのユニットテストで浮動小数点数の比較を行う

こんにちは。meviyのWebシステムを開発しています、大崎です。
主に溶接関連の機能を開発しております。

今回はScalaユニットテストでの浮動小数点数の比較について触れたいと思います。

Scalaユニットテスト浮動小数点数を比較する方法

準備

ScalaTestを使用して、浮動小数点数の比較を行うにあたり、
https://www.scalatest.org/install を参考に、build.sbtなりpom.xmlなりにscalactic及びscalatestの依存関係を追加してください。

ScalaTestでの浮動小数点数の比較

ScalaTestは、Scalaでのユニットテストを簡潔に書くためのフレームワークです。

早速、サンプルコードを読んでみましょう。

import org.scalatest.diagrams.Diagrams
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec

class FloatingPointSpec extends AnyWordSpec with Matchers with Diagrams {
  "浮動小数点数の計算結果が正しく比較できること" in {
    val result = 0.1 + 0.2
    assert(result === 0.3 +- 0.0001)
  }
}

浮動小数点数を比較する際には、===+-演算子を使います。
これらの演算子の説明は以下の通りです。

  • === は2つの値が等しいかどうかを厳密に判定
  • +- は許容される誤差を定義

ちなみにですが、これらの演算子はScalacticというライブラリで定義されている、演算子のように見えるメソッドです。
Scalaでは書き手自身でこうしたメソッドを定義できます。

+- を使わない場合

ここで、 +- という演算子がなければどのようなエラーが発生するのか確認してみましょう。
コードは以下のようになります。

import org.scalatest.diagrams.Diagrams
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec

class FloatingPointSpec extends AnyWordSpec with Matchers with Diagrams {
  "浮動小数点数の計算結果が正しく比較できること" in {
    val result = 0.1 + 0.2
    assert(result === 0.3)
  }
}

実行結果は以下のようになります。テストが失敗していることがわかります。
(余談ですが、Diagramsを用いることで以下のようにassertでの評価の過程を表示させることができます)

assert(result === 0.3)
       |      |   |
       |      |   0.3
       |      false
       0.30000000000000004

ScalaTestFailureLocation: ExampleSpec2 at (ExampleSpec.scala:XX)

エラーメッセージによると、resultが0.30000000000000004となってしまっており、期待値の0.3と等しくないためテストが失敗しています。

このように、浮動小数点数の計算結果は僅かな誤差が含まれる恐れがあり、正確な計算が必要とされている場合には用いることができません。
ただし、ごく小さな誤差が許容される場合は浮動小数点数を用いることが可能です。
その場合は、+-を使い指定した大きさに満たない誤差を無視させることで、浮動小数点数同士の比較を適切に行うことができます。

ちなみに、より精度の高い小数を扱える型としてBigDecimalクラスがあり、GitHub Copilotに聞いたところこちらのような説明をしてくれました→

BigDecimalクラスは、浮動小数点数の計算において丸め誤差を最小限に抑えることができ、金融計算などの高精度が求められる場面で特に有用です。」

JavaJUnitでの浮動小数点数の比較

参考までに、Javaと読み比べて理解を深めたい方もいると思いますので、JavaJUnit 5で浮動小数点数の比較を行うときの例も掲載しておきます。
JUnit 5ではassertEqualsメソッドを使用して、第3引数に精度を指定して比較します。

package org.example;

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;


public class FloatingPointTest {
    @Test
    public void testFloatingPoint() {
        double result = 0.1 + 0.2;
        assertEquals(0.3, result, 0.0001);
    }
}

まとめ

いかがでしょうか。
Scalaでは浮動小数点数の比較が直感的に書けると感じていただけたのではないでしょうか。
このように、ScalaにおいてもJavaと同様に浮動小数点数の比較を行うことができますが、ScalaTest・Scalacticを使用することでより直感的に書くことができます。

終わりに

本記事ではScala浮動小数点数を比較する例を紹介しました。
他にも、ScalaTestには様々な機能があり、それらを駆使することでより簡潔にテストコードを書くことができます。

本ブログでは今後もScalaをはじめとする、DTダイナミクスで用いられている技術やプロダクトなどの魅力を発信していきますので、興味を持っていただけると幸いです。

ほかにも、Scalaに関する記事を書いていますので、ぜひご覧ください。