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

min and max do not conform to IEEE 754 recommendations #5781

Closed
vicuna opened this issue Oct 9, 2012 · 5 comments
Closed

min and max do not conform to IEEE 754 recommendations #5781

vicuna opened this issue Oct 9, 2012 · 5 comments

Comments

@vicuna
Copy link

vicuna commented Oct 9, 2012

Original bug ID: 5781
Status: resolved (set by @xavierleroy on 2012-10-14T08:29:49Z)
Resolution: suspended
Priority: normal
Severity: feature
Version: 4.00.1
Category: standard library
Related to: #4696
Monitored by: mww @hcarty

Bug description

min and max functions are implemented this way in Pervasives:

let min x y = if x <= y then x else y
let max x y = if x >= y then x else y

This gives bad results with floating-point numbers:

max (-0.0) (0.0);;

  • : float = -0. (* wrong *)

max nan infinity;;

  • : float = infinity (* correct *)

max infinity nan;;

  • : float = nan (* wrong: should be infinity (!) according to IEEE 754 since max(infinity, anything) gives infinity for any regular number *)

etc.

@vicuna
Copy link
Author

vicuna commented Oct 10, 2012

Comment author: @gasche

The documentation highlights that min and max do not conform to the specification if one of the parameter is nan : http://caml.inria.fr/pub/docs/manual-ocaml/libref/Pervasives.html#VALmin

I don't think we should make those generic functions any slower by adding tests for the floating-point case. It could make sense to provide floating-point-specific functions with the expected behavior (maybe in one of the extended libraries project, Batteries or Core).

@vicuna
Copy link
Author

vicuna commented Oct 10, 2012

I would prefer the standard library to come with a correct version of min and max instead of one that breaks expected invariants with floats, especially since we are in a situation where a one-liner would anyway save programs whose performance suffers from a "too safe" definition of min and max.
Notice that the warning in the documentation is not sufficient: as suggested by my examples, we currently have situations where f(min(x, y)) > f(max(x, y)) where f is an increasing function (for instance copysign 1.0). The warning should also mention the sign of 0.0 as a possible cause of unexpected behavior.

@vicuna
Copy link
Author

vicuna commented Oct 14, 2012

Comment author: @xavierleroy

According to the latest (2008) version of the IEEE-754 standard (p. 19, functions minNum and maxNum), minNum x y and maxNum x y can return either x or y if x and y compare equal. So, max (-0.0) 0.0 = -0.0 is correct according to IEEE-754. If you don't agree with this behavior, you should take the issue with the IEEE-754 committee.

Concerning NaN behavior, IEEE-754-2008 agrees with the behavior you expected (NaN, non-NaN -> non-NaN), which corresponds to the "NaN as missing data" view. Note that the other behavior (NaN, non-NaN -> NaN) was also requested (see #4696), as it makes more sense if we view NaN as an error that must propagate upwards.

A problem with implementing the minNum/maxNum NaN behavior is that OCaml's min and max functions are polymorphic and apply at any type where generic comparison is defined. So, for example

min (1.0, 2.0) (3.0, 0.0) = (1.0, 2.0)

This raises the case, not covered by IEEE-754, where both arguments contain NaNs but are different:

min (nan, 1.0) (1.0, nan)

in which case there is no "best" choice of result.

The closest we could get to the IEEE-754 behavior is to guarantee that if one argument contains a NaN and the other contains no NaNs, the latter is returned. Implementing this behavior on top of the current generic comparison operator would require two comparisons instead of one, and that's a big increase in running time because the generic comparison is quite slow.

For all these reasons (run-time costs, polymorphism of min and max, lack of agreement whether min and max should be strict or lenient in NaN arguments), I think the current documented behavior of min and max wrt NaN is reasonable.

@vicuna vicuna closed this as completed Oct 14, 2012
@vicuna
Copy link
Author

vicuna commented Oct 15, 2012

Notice that I haven't said max (-0.0) 0.0 returning -0.0 is not IEEE-754-compliant (the name I gave to this issue was a bit misleading, sorry). BTW the same issue exists even for functions such as sqrt (although reasonable people would certainly expect its argument and result to be non-negative!). I would just have preferred Ocaml to adopt a robust behavior by avoiding breaking invariants such as the one I mentioned (since there is room for this case in IEEE-754). After all, correctness is the /raison d'être/ of OCaml, right?
I forgot the "min and max of structure" feature of Ocaml and I understand that it is an issue as far as NaN is concerned.

@vicuna
Copy link
Author

vicuna commented Apr 7, 2017

Comment author: travm1

Here is some more info about the IEEE 754 standard
https://www.everipedia.com/IEEE_floating_point/

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

No branches or pull requests

1 participant