Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Double "close-to" matcher that uses the ULP. #402

Open
garretwilson opened this issue Aug 1, 2023 · 0 comments
Open

Double "close-to" matcher that uses the ULP. #402

garretwilson opened this issue Aug 1, 2023 · 0 comments

Comments

@garretwilson
Copy link

garretwilson commented Aug 1, 2023

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).

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 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant