2

I am trying to create a custom matcher that will take into account Double.NaN and will use tolerance for non-nan values.

import org.scalactic.{Equality, TolerantNumerics}
import org.scalatest.Matchers

trait MoreMatchers extends Matchers {

  implicit def doubleEqWithNaNAndTol: Equality[Double] = new Equality[Double] {

    implicit val tolerance: Equality[Double] = TolerantNumerics.tolerantDoubleEquality(1e-6)

    def areEqual(expected: Double, actual: Any): Boolean = actual match {
      case number: Double if number.isNaN => expected.isNaN
      case _: Double => actual === expected
      case _ => false
    }

  }

Unfortunately, it doesn't work.

assert(0.00226685508536916 === 0.0022668550853691587)  // failure - doesn't use tolerance
assert (Double.NaN === Double.NaN )  // success

If enter the tolerance within the assertion then it fails if there are NaN.

assert(0.00226685508536916 === 0.0022668550853691587 +- 1e-6)  // success
assert (Double.NaN === Double.NaN  +- 1e-6)  // failure - can't use tolerance when NaN

If I just call it like the following, then it works.

implicit val tolerance: Equality[Double] = TolerantNumerics.tolerantDoubleEquality(1e-6)

def eq(expected: Double, actual: Double): Boolean = expected match {
    case x if x.isNaN => actual.isNaN
    case _ => actual === expected
}

And then call it as:

assert(eq(...,...))

I am wondering if it is possible to get it working using the first way. Have you come across such case before? Could you suggest any solutions? Any help will be appreciated :)

Thanks, ele

ele
  • 471
  • 1
  • 5
  • 15

1 Answers1

0

Many thanks to eirikr @d6 :), see solution in custom equality gist

Basically in the above code you need to use tolerance.areEqual(expected, number) when comparing non-nan doubles in order to be able to use the tolerance implicit in that comparison.

import org.scalactic.{Equality, TolerantNumerics}
import org.scalatest.Matchers

trait MoreMatchers extends Matchers {

  implicit def doubleEqWithNaNAndTol: Equality[Double] = new Equality[Double] {

    implicit val tolerance: Equality[Double] = TolerantNumerics.tolerantDoubleEquality(1e-6)

    def areEqual(expected: Double, actual: Any): Boolean = actual match {
      case x: Double if x.isNaN => expected.isNaN
      case x: Double => tolerance.areEqual(expected, x)
      case _ => false
    }

  }

Regards, ele

ele
  • 471
  • 1
  • 5
  • 15