summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBodigrim <andrew.lelechenko@gmail.com>2023-04-23 20:54:00 +0100
committerMarge Bot <ben+marge-bot@smart-cactus.org>2023-05-03 20:16:57 -0400
commitf3460845cdd37cf5a086cc02336c35310246b725 (patch)
tree973562ec4ac981e7bb8b57400f3c362446dcaefb
parent931c8d82f28fb98a7e0ad0a837eff05c08021cbe (diff)
downloadhaskell-f3460845cdd37cf5a086cc02336c35310246b725.tar.gz
Document instances of Double
-rw-r--r--libraries/base/GHC/Float.hs118
-rw-r--r--libraries/base/GHC/Real.hs4
2 files changed, 94 insertions, 28 deletions
diff --git a/libraries/base/GHC/Float.hs b/libraries/base/GHC/Float.hs
index 012de47689..195c23b1f4 100644
--- a/libraries/base/GHC/Float.hs
+++ b/libraries/base/GHC/Float.hs
@@ -276,17 +276,18 @@ class (RealFrac a, Floating a) => RealFloat a where
------------------------------------------------------------------------
-- | @since 2.01
--- Note that due to the presence of @NaN@, not all elements of 'Float' have an
--- additive inverse.
--
--- >>> 0/0 + (negate 0/0 :: Float)
--- NaN
+-- This instance implements IEEE 754 standard with all its usual pitfalls
+-- about NaN, infinities and negative zero.
+-- Neither addition not multiplication are associative or distributive:
--
--- Also note that due to the presence of -0, `Float`'s 'Num' instance doesn't
--- have an additive identity
+-- >>> (0.1 + 0.1 :: Float) + 0.5 == 0.1 + (0.1 + 0.5)
+-- False
+-- >>> (0.1 + 0.2 :: Float) * 0.9 == 0.1 * 0.9 + 0.2 * 0.9
+-- False
+-- >>> (0.1 * 0.1 :: Float) * 0.9 == 0.1 * (0.1 * 0.9)
+-- False
--
--- >>> 0 + (-0 :: Float)
--- 0.0
instance Num Float where
(+) x y = plusFloat x y
(-) x y = minusFloat x y
@@ -317,6 +318,14 @@ naturalToFloat# (NB b) = case integerToBinaryFloat' (IP b) of
F# x -> x
-- | @since 2.01
+--
+-- Beware that 'toRational' generates garbage for non-finite arguments:
+--
+-- >>> toRational (1/0 :: Float)
+-- 340282366920938463463374607431768211456 % 1
+-- >>> toRational (0/0 :: Float)
+-- 510423550381407695195061911147652317184 % 1
+--
instance Real Float where
toRational (F# x#) =
case decodeFloat_Int# x# of
@@ -330,14 +339,19 @@ instance Real Float where
IS m# :% integerShiftL# 1 (int2Word# (negateInt# e#))
-- | @since 2.01
--- Note that due to the presence of @NaN@, not all elements of 'Float' have an
--- multiplicative inverse.
--
--- >>> 0/0 * (recip 0/0 :: Float)
--- NaN
+-- This instance implements IEEE 754 standard with all its usual pitfalls
+-- about NaN, infinities and negative zero.
+--
+-- >>> 0 == (-0 :: Float)
+-- True
+-- >>> recip 0 == recip (-0 :: Float)
+-- False
+-- >>> map (/ 0) [-1, 0, 1 :: Float]
+-- [-Infinity,NaN,Infinity]
+-- >>> map (* 0) $ map (/ 0) [-1, 0, 1 :: Float]
+-- [NaN,NaN,NaN]
--
--- Additionally, because of @NaN@ this instance does not obey the left-inverse
--- law for 'toRational'/'fromRational'.
instance Fractional Float where
(/) x y = divideFloat x y
{-# INLINE fromRational #-}
@@ -360,6 +374,15 @@ rationalToFloat n d
mantDigs = FLT_MANT_DIG
-- | @since 2.01
+--
+-- Beware that results for non-finite arguments are garbage:
+--
+-- >>> [ f x | f <- [round, floor, ceiling], x <- [-1/0, 0/0, 1/0 :: Float] ] :: [Int]
+-- [0,0,0,0,0,0,0,0,0]
+-- >>> map properFraction [-1/0, 0/0, 1/0] :: [(Int, Float)]
+-- [(0,0.0),(0,0.0),(0,0.0)]
+--
+-- and get even more non-sensical if you ask for 'Integer' instead of 'Int'.
instance RealFrac Float where
properFraction = properFractionFloat
@@ -507,17 +530,18 @@ instance Show Float where
------------------------------------------------------------------------
-- | @since 2.01
--- Note that due to the presence of @NaN@, not all elements of 'Double' have an
--- additive inverse.
--
--- >>> 0/0 + (negate 0/0 :: Double)
--- NaN
+-- This instance implements IEEE 754 standard with all its usual pitfalls
+-- about NaN, infinities and negative zero.
+-- Neither addition not multiplication are associative or distributive:
--
--- Also note that due to the presence of -0, `Double`'s 'Num' instance doesn't
--- have an additive identity
+-- >>> (0.1 + 0.1) + 0.4 == 0.1 + (0.1 + 0.4)
+-- False
+-- >>> (0.1 + 0.2) * 0.3 == 0.1 * 0.3 + 0.2 * 0.3
+-- False
+-- >>> (0.1 * 0.1) * 0.3 == 0.1 * (0.1 * 0.3)
+-- False
--
--- >>> 0 + (-0 :: Double)
--- 0.0
instance Num Double where
(+) x y = plusDouble x y
(-) x y = minusDouble x y
@@ -550,6 +574,14 @@ naturalToDouble# (NB b) = case integerToBinaryFloat' (IP b) of
-- | @since 2.01
+--
+-- Beware that 'toRational' generates garbage for non-finite arguments:
+--
+-- >>> toRational (1/0)
+-- 179769313 (and 300 more digits...) % 1
+-- >>> toRational (0/0)
+-- 269653970 (and 300 more digits...) % 1
+--
instance Real Double where
toRational (D# x#) =
case integerDecodeDouble# x# of
@@ -563,14 +595,19 @@ instance Real Double where
m :% integerShiftL# 1 (int2Word# (negateInt# e#))
-- | @since 2.01
--- Note that due to the presence of @NaN@, not all elements of 'Double' have an
--- multiplicative inverse.
--
--- >>> 0/0 * (recip 0/0 :: Double)
--- NaN
+-- This instance implements IEEE 754 standard with all its usual pitfalls
+-- about NaN, infinities and negative zero.
+--
+-- >>> 0 == (-0 :: Double)
+-- True
+-- >>> recip 0 == recip (-0 :: Double)
+-- False
+-- >>> map (/ 0) [-1, 0, 1]
+-- [-Infinity,NaN,Infinity]
+-- >>> map (* 0) $ map (/ 0) [-1, 0, 1]
+-- [NaN,NaN,NaN]
--
--- Additionally, because of @NaN@ this instance does not obey the left-inverse
--- law for 'toRational'/'fromRational'.
instance Fractional Double where
(/) x y = divideDouble x y
{-# INLINE fromRational #-}
@@ -626,6 +663,15 @@ instance Floating Double where
{-# INLINE log1pexp #-}
-- | @since 2.01
+--
+-- Beware that results for non-finite arguments are garbage:
+--
+-- >>> [ f x | f <- [round, floor, ceiling], x <- [-1/0, 0/0, 1/0] ] :: [Int]
+-- [0,0,0,0,0,0,0,0,0]
+-- >>> map properFraction [-1/0, 0/0, 1/0] :: [(Int, Double)]
+-- [(0,0.0),(0,0.0),(0,0.0)]
+--
+-- and get even more non-sensical if you ask for 'Integer' instead of 'Int'.
instance RealFrac Double where
properFraction = properFractionDouble
truncate = truncateDouble
@@ -796,6 +842,14 @@ for these (@numericEnumFromTo@ and @numericEnumFromThenTo@ below.)
-}
-- | @since 2.01
+--
+-- 'fromEnum' just truncates its argument, beware of all sorts of overflows.
+--
+-- List generators have extremely peculiar behavior, mandated by
+-- [Haskell Report 2010](https://www.haskell.org/onlinereport/haskell2010/haskellch6.html#x13-1310006.3.4):
+--
+-- >>> [0..1.5 :: Float]
+-- [0.0,1.0,2.0]
instance Enum Float where
succ x = x + 1
pred x = x - 1
@@ -807,6 +861,14 @@ instance Enum Float where
enumFromThenTo = numericEnumFromThenTo
-- | @since 2.01
+--
+-- 'fromEnum' just truncates its argument, beware of all sorts of overflows.
+--
+-- List generators have extremely peculiar behavior, mandated by
+-- [Haskell Report 2010](https://www.haskell.org/onlinereport/haskell2010/haskellch6.html#x13-1310006.3.4):
+--
+-- >>> [0..1.5]
+-- [0.0,1.0,2.0]
instance Enum Double where
succ x = x + 1
pred x = x - 1
diff --git a/libraries/base/GHC/Real.hs b/libraries/base/GHC/Real.hs
index 6fa49f89e2..8983cf10e3 100644
--- a/libraries/base/GHC/Real.hs
+++ b/libraries/base/GHC/Real.hs
@@ -142,6 +142,10 @@ denominator (_ :% y) = y
--
-- [__Coherence with 'fromRational'__]: if the type also implements 'Fractional',
-- then 'fromRational' is a left inverse for 'toRational', i.e. @fromRational (toRational i) = i@
+--
+-- The law does not hold for 'Float', 'Double', 'Foreign.C.Types.CFloat',
+-- 'Foreign.C.Types.CDouble', etc., because these types contain non-finite values,
+-- which cannot be roundtripped through 'Rational'.
class (Num a, Ord a) => Real a where
-- | the rational equivalent of its real argument with full precision
toRational :: a -> Rational