elm-verify-examples icon indicating copy to clipboard operation
elm-verify-examples copied to clipboard

Impossible to test equality of floating point numbers

Open jewiet opened this issue 5 years ago • 7 comments

Hey!

Thanks for the tool.

I want to use elm-verify-examples to test a function that returns a float. It seems impossible to test equality of floating point numbers, even if they are strictly equal. Consider the following example:

0.1 --> 0.1

With this I am getting the following message: Do not use Expect.equal with floats. Use Float.within instead.

jewiet avatar Apr 23 '19 10:04 jewiet

Generally speaking I think if you are going to be using floating point numbers in examples, then they should be very close matches indeed. As such, if we knew what was a floating point test, we could pick some fairly reasonable values for the Expect.within call (and perhaps those could even be configurable in elm-verify-examples.json).

However, the main issue is working out when to use floating point comparison. I think there are a few options, but they all have downsides:

  1. We attempt to detect floating point numbers by syntax. This would mean that if you write --> 3.0 this would work, however, if you wrote --> 2/5 this would not. 😒
  2. We introduce special syntax for this --~ (say). 😒
  3. We politely ask elm-test maintainers (@Janiczek) to perhaps revert this decision and allow exact equality for floats. Personally I think the current solution is a bit ham-fisted and perhaps a future api more like https://github.com/elm-explorations/test/issues/216 would be a better solution to the problem elm-test is trying to solve - as direct floating point equality is prohibited and good alternative exists, but floating points contained in data structures have no real solution in elm-test.

gampleman avatar Oct 20 '23 13:10 gampleman

For what it's worth I don't think option 1 is so bad...are there actually many cases where you might want to write a fraction?

One nice thing about option 1 is that then you don't have to worry about having an arbitrary tolerance, you can instead check whether the actual numeric result formatted to the same number of decimal places used in the example result gives the same string as the example result. Literally test that if you ran the example code and printed out the result to the same number of decimal places, would you get the same output...

I guess you could argue that that that could be affected by rounding settings, but you could accomplish pretty much the same thing by using a tolerance set to 10^-n where n is the number of decimal digits shown in the example result. (e.g. if an example shows an output of 2.34, then that should definitely be accurate to within 0.01...)

ianmackenzie avatar Oct 23 '23 02:10 ianmackenzie

I think 1. is bad mostly for the unintuitive behaviour:

  • 1 would behave differently from 1.0. This is not the same as how elm behaves.
  • foo bar --> sin bar or some such may be a perfectly sensible way to describe the result (remember the goal here is to communicate)

Deriving the precision from the number of decimal digits is a good idea though!

My preferred option would be 3, but failing that 1 is probably the best way forward.

gampleman avatar Oct 23 '23 09:10 gampleman

Option 3 is pretty annoying if you have examples where the result is (unavoidably) irrational though...if you wanted to write out an example for a circle area or sphere volume function you'd need to include all 17 decimal places or whatever if you wanted the parsed number to be exactly equal to the actual result. Even something like (famously)

0.1 + 0.2 --> 0.3

doesn't work, you'd have to write

0.1 + 0.2 --> 0.30000000000000004

which seems...not ideal.

ianmackenzie avatar Oct 25 '23 01:10 ianmackenzie

The foo bar --> sin bar thing is hard to figure out how to deal with though...I think the only not-overly-magical solution there is to just specify a floating point tolerance in elm-verify-examples.json.

ianmackenzie avatar Oct 25 '23 01:10 ianmackenzie

We could also specify the precision per example, something like

-- Precision: 1
0.1 + 0.2 --> 0.3

stoeffel avatar Oct 25 '23 04:10 stoeffel

The problem isn't (only) specifying the tolerance there, but even knowing that you should be using floating point equality.

0.1 + 0.2 --> 0.30000000000000004

I personally think that's fine, since at least its accurate. Like if someone copies 0.1 + 0.2 into elm repl, they will get exactly what it says in the docs. Now you as a documentation writer might not like having to specify so many decimal digits, but perhaps you could just pick a nicer example?

gampleman avatar Oct 25 '23 07:10 gampleman