I personally think the inclusion of Float and Double in Enum is an unmitigated disaster. Enum consists of three separate parts: (1) succ & pred. These appear for float to correspond to adding or subtracting 1.0. (I am finding this out by testing with ghci; it's not specified where it should be, in section 6.3.4 of the standard). Because of rounding errors, succ and pred fail many of the properties for float they have for integers, EG succ . pred = pred . succ = id succ x > x and so on. succ and pred might actually be USEFUL if instead they represented the next representable real (with succ (-0.0) = 0.0 I suppose), as it is I don't really need succ when (+1.0) is clearer and not much longer to type. (2) toEnum,fromEnum. fromEnum appears to do some unspecified rounding; for GHC it rounds to 0. toEnum also does rounding, since Float has in some areas less precision than integers. For example (toEnum 1234567890) :: Float and (toEnum 1234567891) :: Float appear to be identical. Why we should need two poorly-specified rounding functions here is beyond me. (3) enumFrom, enumFromThen and so on, aka arithmetic sequences. These are defined in terms of equations which work for exact types, but to find out how it actually works you need to decipher the Haskell code in the prelude, which tells you the increment is added at each step. Thus the second element of [m,n...] may not be n, and [x..] will eventually get stuck in a loop. I think it would be best if we could just get rid of using the Enum instance for Float and Double, but if not it should be deprecated.
George Russell <ger@tzi.de> writes:
(1) succ & pred. These appear for float to correspond to adding or subtracting 1.0. (I am finding this out by testing with ghci; it's not specified where it should be, in section 6.3.4 of the standard). Because of rounding errors, succ and pred fail many of the properties for float they have for integers, EG succ . pred = pred . succ = id succ x > x and so on.
Quite a few pbs with overflow on Int too: i :: Int i = 0x7fffffff i_plus_1 = i+1 -- ghc : -2147483648 -- hugs: -2147483648 i_succ = succ i -- ghc : *** Exception: Prelude.Enum.succ{Int}: tried to take `succ' of maxBound -- hugs: -2147483648 j :: Int j = 0x80000000 -- ghc : -2147483648 -- hugs: Program error: {primIntegerToInt 2147483648} k :: Int k = 0x100000000 -- ghc : 0 -- hugs: Program error: {primIntegerToInt 4294967296} i':: Integer i'= 0x7fffffff i_plus_1' = i+1 -- ghc : 2147483648 -- hugs: 2147483648 i_succ' = succ i' -- ghc : 2147483648 -- hugs: -2147483648
Pixel <pixel@mandrakesoft.com> writes:
Quite a few pbs with overflow on Int too:
i :: Int i = 0x7fffffff
i_plus_1 = i+1 -- ghc : -2147483648 -- hugs: -2147483648
Given that Int represents modulo arithmetic, this is all right, but
i_succ = succ i -- ghc : *** Exception: Prelude.Enum.succ{Int}: tried to take `succ' of maxBound -- hugs: -2147483648
GHC's behavior here is inconsequent, I think. I suppose it is trying to maintain the invariant succ i > i
j :: Int j = 0x80000000 -- ghc : -2147483648 -- hugs: Program error: {primIntegerToInt 2147483648}
I'm not sure which behavior I prefer. You're specifying a constant out of range for the datatype.
k :: Int k = 0x100000000 -- ghc : 0 -- hugs: Program error: {primIntegerToInt 4294967296}
Same as above.
i':: Integer i'= 0x7fffffff i_plus_1' = i+1 -- ghc : 2147483648 -- hugs: 2147483648
i_succ' = succ i' -- ghc : 2147483648 -- hugs: -2147483648
I think Hugs is wrong. Integer shouldn't wrap. The greatest problem with Integer seem to be different implementations, IMHO. -kzm -- If I haven't seen further, it is by standing in the footprints of giants
I just thought I should add the results for hbc and nhc98 to this enumeration of woes, to further illustrate the difficulties.
i :: Int i = 0x7fffffff
i_plus_1 = i+1 -- ghc : -2147483648 -- hugs: -2147483648 -- nhc98: -2147483648 -- hbc: -2147483648
i_succ = succ i -- ghc : *** Exception: Prelude.Enum.succ{Int}: tried to take `succ' of maxBound -- hugs: -2147483648 -- nhc98: -2147483648 -- hbc: -2147483648
j :: Int j = 0x80000000 -- ghc : -2147483648 -- hugs: Program error: {primIntegerToInt 2147483648} -- nhc98: -2147483648 (+ warning: decimal constant is so large that it is unsigned) -- hbc: -2147483648
k :: Int k = 0x100000000 -- ghc : 0 -- hugs: Program error: {primIntegerToInt 4294967296} -- nhc98: 0 -- hbc: 0
i':: Integer i'= 0x7fffffff i_plus_1' = i+1 -- ghc : 2147483648 -- hugs: 2147483648 -- nhc98: 2147483648 -- hbc: 2147483648
i_succ' = succ i' -- ghc : 2147483648 -- hugs: -2147483648 -- nhc98: -2147483648 -- hbc: -2147483648
I think Hugs is wrong. Integer shouldn't wrap.
(Actually, ghc is `wrong': hbc, hugs and nhc98 match the Report's specification here: succ = toEnum . (+1) . fromEnum This is confirmed by the description of the semantics in section 3.10.)
succ 1.45 -- hugs: 2.0 -- ghci: 2.45 -- nhc98: 2.0000000000000000 -- hbc: 2.0
succ 1.99 -- hugs: 2.0 -- ghci: 2.99 -- nhc98: 2.0000000000000000 -- hbc: 2.0
succ 1.99999 -- hugs: 2.0 -- ghci: 2.99999 -- nhc98: 2.0000000000000000 -- hbc: 2.0
succ (-0.2) -- hugs: 1.0 -- ghci: 0.8 -- nhc98: 1.0000000000000000 -- hbc: 1.0
succ (-0.99999) -- hugs: 1.0 -- ghci: 9.99999999995449e-6 -- nhc98: 1.0000000000000000 -- hbc: 1.0
succ (-1.1) -- hugs: 0.0 -- ghci: -0.10000000000000009 -- nhc98: 0.0000000000000000 -- hbc: 0.0
Ghc does not follow the Report here either.
[1.0..2.5] -- hugs: [1.0,2.0,3.0] -- ghci: [1.0,2.0,3.0] -- nhc98: [1.0000000000000000,2.0000000000000000] -- hbc: [1.0,2.0,3.0]
[1.1..2.5] -- hugs: [1.1,2.1] -- ghci: [1.1,2.1] -- nhc98: [1.0000000000000000,2.0000000000000000] -- hbc: [1.1,2.1]
[1.1..3.0] -- hugs: [1.1,2.1,3.1] -- ghci: [1.1,2.1,3.1] -- nhc98: [1.0000000000000000,2.0000000000000000,3.0000000000000000] -- hbc: [1.1,2.1,3.1]
nhc98 is wrong for all the Double arithmetic sequences. Regards, Malcolm
George Russell <ger@tzi.de> writes:
(1) succ & pred. These appear for float to correspond to adding or subtracting 1.0. (I am finding this out by testing with ghci; it's not specified where it should be, in section 6.3.4 of the standard). Because of rounding errors, succ and pred fail many of the properties for float they have for integers, EG succ . pred = pred . succ = id succ x > x and so on.
Here's some interesting results from Hugs: Prelude> succ 1.45 2.0 Prelude> succ 1.99 2.0 Prelude> succ 1.99999 2.0 Prelude> succ (-0.2) 1.0 Prelude> succ (-0.99999) 1.0 Prelude> succ (-1.1) 0.0 So, apparently, succ jumps to the smallest integer larger than the argument for positive argument values, and to the smallest integer larger than the argument plus one for negative values. Or something. Here's a sampling from GHCi: Main> succ 1.45 2.45 Main> succ 1.99 2.99 Main> succ (-0.2) 0.8 Main> succ (-1.1) -0.10000000000000009 Apparently, GHC simply adds 1.0. What was the argument for having Float and Double be part of Enum again? :-) Of course, it's nice to be able to specify sequences with the (..), but (GHC again): Main> [1.0..2.5] [1.0,2.0,3.0] Main> [1.1..2.5] [1.1,2.1] Main> [1.1..3.0] [1.1,2.1,3.1] I'm not sure it is always obvious which numbers you end up with. Some sequences have the last element outside the specified range, some do not. At least, Hugs and GHC seem to be consistent. Personally, I'd be inclined to drop succ and pred from Float/Double, and only allow ranges with a specific increment. (BTW, looking at the Enum definition in the HR, it seems to me that ..-generated lists can't be longer than maxint, since they are defined in terms of fromEnum and toEnum, which works with Ints. Is this correct? Why?) -kzm -- If I haven't seen further, it is by standing in the footprints of giants
participants (4)
-
George Russell -
Ketil Malde -
Malcolm Wallace -
Pixel