Hamcrest Matchers already has a closeTo(double operand, double error) method which allows you to specify an error for fuzzy equals with floating point values. (See How do I compare doubles using JUnit and Hamcrest?.)
The problem is that the error component is usually arbitrary. Do I put 0.1? Do I put 0.00001? There is actually a correct answer here (more accurately, there are some values that are orders of magnitude more reasonable than others), but the typical programmer doesn't know this, and those that would probably don't take the time to investigate it. So the tests wind up with some arbitrary error value such as 0.001. And the tests work. And nobody is harmed. But what was the point of adding an arbitrary value, wasting time, and cluttering the code, if many other arbitrary values would have worked?
It turns out that the amount of error to accept depends on the actual expected value based upon how floating point numbers are stored. In Java 5, the Math.ulp(double d) method was added:
Returns the size of an ulp of the argument. An ulp, unit in the last place, of a double value is the positive distance between this floating-point value and the double value next larger in magnitude. Note that for non-NaN x, ulp(-x) == ulp(x).
I'm not a math expert, but the ULP sounds as good as any to serve as an error component; this Stack Overflow answer thinks so too. It's explained in more detail in this other answer.
I thus propose that you add a closeTo(double operand) that simply delegates to closeTo(double operand, double error) using Math.ulp(double d) as the error value. Developers will have to type less. The code will be cleaner. Developers will have to think less, and in this case thinking less is good, because in this scenario developers usually only think enough to put in some arbitrary value, not some meaningful value. And in 99.999999999% of cases (with an error component of … never mind), the error value used will be more meaningful in real life than the one the developer pull off the top of their head.
See my Doubles.equalsLenient() methods as an example of how I'm already using the ULP in my own comparisons. I'm using this with Junit, too—I'd just rather continue using assertThat() instead of switching to assertTrue() and adding extra method calls in my unit tests.
Hamcrest
Matchers
already has acloseTo(double operand, double error)
method which allows you to specify an error for fuzzy equals with floating point values. (See How do I compare doubles using JUnit and Hamcrest?.)The problem is that the error component is usually arbitrary. Do I put
0.1
? Do I put0.00001
? There is actually a correct answer here (more accurately, there are some values that are orders of magnitude more reasonable than others), but the typical programmer doesn't know this, and those that would probably don't take the time to investigate it. So the tests wind up with some arbitrary error value such as0.001
. And the tests work. And nobody is harmed. But what was the point of adding an arbitrary value, wasting time, and cluttering the code, if many other arbitrary values would have worked?It turns out that the amount of error to accept depends on the actual expected value based upon how floating point numbers are stored. In Java 5, the
Math.ulp(double d)
method was added:See also Can someone explain the Math.ulp(double) method?.
I'm not a math expert, but the ULP sounds as good as any to serve as an error component; this Stack Overflow answer thinks so too. It's explained in more detail in this other answer.
I thus propose that you add a
closeTo(double operand)
that simply delegates tocloseTo(double operand, double error)
usingMath.ulp(double d)
as the error value. Developers will have to type less. The code will be cleaner. Developers will have to think less, and in this case thinking less is good, because in this scenario developers usually only think enough to put in some arbitrary value, not some meaningful value. And in 99.999999999% of cases (with an error component of … never mind), the error value used will be more meaningful in real life than the one the developer pull off the top of their head.See my
Doubles.equalsLenient()
methods as an example of how I'm already using the ULP in my own comparisons. I'm using this with Junit, too—I'd just rather continue usingassertThat()
instead of switching toassertTrue()
and adding extra method calls in my unit tests.