With Ghc/Ghci, version 8.8.4, I get this:
[0,2..3] evaluates to [0,2], while [0.0,2..3] evaluates to [0.0,2.0,4.0]
What is going on?
With Ghc/Ghci, version 8.8.4, I get this:
[0,2..3] evaluates to [0,2], while [0.0,2..3] evaluates to [0.0,2.0,4.0]
What is going on?
[a, b..c]
is syntactic sugar for enumFromThenTo a b c
, so we should look at its definition for Double
:
instance Enum Double where
succ x = x + 1
pred x = x - 1
toEnum = int2Double
fromEnum = fromInteger . truncate -- may overflow
enumFrom = numericEnumFrom
enumFromTo = numericEnumFromTo
enumFromThen = numericEnumFromThen
enumFromThenTo = numericEnumFromThenTo
It just uses numericEnumFromThenTo
, which is defined as:
numericEnumFromThen :: (Fractional a) => a -> a -> [a]
numericEnumFromThen n m = go 0
where
step = m - n
-- See Note [Numeric Stability of Enumerating Floating Numbers]
go !k = let !n' = n + k * step
in n' : go (k + 1)
numericEnumFromThenTo :: (Ord a, Fractional a) => a -> a -> a -> [a]
numericEnumFromThenTo e1 e2 e3
= takeWhile predicate (numericEnumFromThen e1 e2)
where
mid = (e2 - e1) / 2
predicate | e2 >= e1 = (<= e3 + mid)
| otherwise = (>= e3 + mid)
So, numericEnumFromThen
([a, b..]
) simply repeatedly adds a step to a starting point, which is pretty intuitive. However, numericEnumFromThenTo
is a bit special: it actually stops after you pass the end value by more than half the step. It does not stop immediately after reaching the end value. This was probably made to deal with floating point imprecision, but it does lead to some weird behavior like you see here.