summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBodigrim <andrew.lelechenko@gmail.com>2022-12-21 20:35:02 +0000
committerMarge Bot <ben+marge-bot@smart-cactus.org>2022-12-24 21:10:34 -0500
commita5bd0eb8dd1d03c54e1b0b476ebbc4cc886d6f19 (patch)
treea3210c220dc049f06a8a086b95dc45e160d28a2f
parent6d7d4393e15a6d0a2b42ec986ec6bd3df27baaa7 (diff)
downloadhaskell-a5bd0eb8dd1d03c54e1b0b476ebbc4cc886d6f19.tar.gz
Document infelicities of instance Ord Double and workarounds
-rw-r--r--libraries/ghc-prim/GHC/Classes.hs53
1 files changed, 30 insertions, 23 deletions
diff --git a/libraries/ghc-prim/GHC/Classes.hs b/libraries/ghc-prim/GHC/Classes.hs
index 9b24ebcb16..9a76e679d3 100644
--- a/libraries/ghc-prim/GHC/Classes.hs
+++ b/libraries/ghc-prim/GHC/Classes.hs
@@ -410,19 +410,7 @@ instance Ord Char where
(C# c1) <= (C# c2) = isTrue# (c1 `leChar#` c2)
(C# c1) < (C# c2) = isTrue# (c1 `ltChar#` c2)
--- | Note that due to the presence of @NaN@, `Float`'s 'Ord' instance does not
--- satisfy reflexivity.
---
--- >>> 0/0 <= (0/0 :: Float)
--- False
---
--- Also note that, due to the same, `Ord`'s operator interactions are not
--- respected by `Float`'s instance:
---
--- >>> (0/0 :: Float) > 1
--- False
--- >>> compare (0/0 :: Float) 1
--- GT
+-- | See @instance@ 'Ord' 'Double' for discussion of deviations from IEEE 754 standard.
instance Ord Float where
(F# x) `compare` (F# y)
= if isTrue# (x `ltFloat#` y) then LT
@@ -434,19 +422,38 @@ instance Ord Float where
(F# x) >= (F# y) = isTrue# (x `geFloat#` y)
(F# x) > (F# y) = isTrue# (x `gtFloat#` y)
--- | Note that due to the presence of @NaN@, `Double`'s 'Ord' instance does not
--- satisfy reflexivity.
+-- | IEEE 754 'Double'-precision type includes not only numbers, but also
+-- positive and negative infinities and a special element called @NaN@
+-- (which can be quiet or signal).
--
--- >>> 0/0 <= (0/0 :: Double)
--- False
+-- IEEE 754-2008, section 5.11 requires that if at least one of arguments of
+-- '<=', '<', '>', '>=' is @NaN@ then the result of the comparison is 'False',
+-- and @instance@ 'Ord' 'Double' complies with this requirement. This violates
+-- the reflexivity: both @NaN@ '<=' @NaN@ and @NaN@ '>=' @NaN@ are 'False'.
--
--- Also note that, due to the same, `Ord`'s operator interactions are not
--- respected by `Double`'s instance:
+-- IEEE 754-2008, section 5.10 defines @totalOrder@ predicate. Unfortunately,
+-- 'compare' on 'Double's violates the IEEE standard and does not define a total order.
+-- More specifically, both 'compare' @NaN@ @x@ and 'compare' @x@ @NaN@ always return 'GT'.
+--
+-- Thus, users must be extremely cautious when using @instance@ 'Ord' 'Double'.
+-- For instance, one should avoid ordered containers with keys represented by 'Double',
+-- because data loss and corruption may happen. An IEEE-compliant 'compare' is available
+-- in @fp-ieee@ package as @TotallyOrdered@ newtype.
+--
+-- Moving further, the behaviour of 'min' and 'max' with regards to @NaN@ is
+-- also non-compliant. IEEE 754-2008, section 5.3.1 defines that quiet @NaN@
+-- should be treated as a missing data by @minNum@ and @maxNum@ functions,
+-- for example, @minNum(NaN, 1) = minNum(1, NaN) = 1@. Some languages such as Java
+-- deviate from the standard implementing @minNum(NaN, 1) = minNum(1, NaN) = NaN@.
+-- However, 'min' / 'max' in @base@ are even worse: 'min' @NaN@ 1 is 1, but 'min' 1 @NaN@
+-- is @NaN@.
+--
+-- IEEE 754-2008 compliant 'min' / 'max' can be found in @ieee754@ package under
+-- @minNum@ / @maxNum@ names. Implementations compliant with
+-- @minimumNumber@ / @maximumNumber@ from a newer
+-- [IEEE 754-2019](https://grouper.ieee.org/groups/msc/ANSI_IEEE-Std-754-2019/background/),
+-- section 9.6 are available from @fp-ieee@ package.
--
--- >>> (0/0 :: Double) > 1
--- False
--- >>> compare (0/0 :: Double) 1
--- GT
instance Ord Double where
(D# x) `compare` (D# y)
= if isTrue# (x <## y) then LT