[GHC] #1026: coarbitrary for Double and Float

GHC trac at galois.com
Fri Nov 24 06:54:47 EST 2006


#1026: coarbitrary for Double and Float
------------------------------------------------------------+---------------
    Reporter:  Susumu Katayama <skata at cs.miyazaki-u.ac.jp>  |       Owner:            
        Type:  bug                                          |      Status:  new       
    Priority:  normal                                       |   Milestone:            
   Component:  libraries (other)                            |     Version:  6.6       
    Severity:  normal                                       |    Keywords:  QuickCheck
  Difficulty:  Easy (1 hr)                                  |    Testcase:            
Architecture:  Multiple                                     |          Os:  Multiple  
------------------------------------------------------------+---------------
@coarbitrary@ methods for Double and Float defined in Test.QuickCheck are
 not usable.

 With GHC 6.6 they either consume unrealistically great amount of time and
 space, or cause:
 *** Exception: Prelude.(!!): negative index

 With the HEAD compiler, it soon causes great heap consumption and soon
 fails with stack overflow.

 == Example: ==
 The following code gobbles the heap space quickly, so please set the
 maximum heap size limit, or be prepared to press CTRL-C when trying it.
 {{{
 \begin{code}
 import Test.QuickCheck
 import Control.Monad.Fix(fix)

 instance Show (a->b) where
     showsPrec _ f = ("fun"++)

 cata (x,f) 0 = x
 cata (x,f) i = f (cata (x,f) (i-1))

 kata c (x,f) 0 = x
 kata c (x,f) i = f (c (x,f) (i-1))

 prop_cata :: (Eq a, Show a) => Int -> a -> (a->a) -> Property
 prop_cata = \i x f -> i>=0 ==> cata (x,f) i == fix kata (x,f) i

 main = do verboseCheck (prop_cata :: Int -> Int   -> (Int  ->Int)   ->
 Property)
           verboseCheck (prop_cata :: Int -> Float -> (Float->Float) ->
 Property)
 \end{code}
 }}}
 (There can be a simpler example, but I do not think that is necessary
 because I have already tracked down the problem.)

 == Reason: ==
 The coarbitrary methods for Float and Double are defined as:
 {{{
   coarbitrary x = coarbitrary (decodeFloat x)
 }}}
 where the first return value (:: Integer) of @decodeFloat :: a ->
 (Integer,Int)@ usually exceeds @maxBound::Int at .
 It will eventually be converted into Int while computing the coarbitrary
 method for Integer, defined as:
 {{{
   coarbitrary n = variant (fromInteger (if n >= 0 then 2*n else 2*(-n) +
 1))
 }}}
 Because @(if n >= 0 then 2*n else 2*(-n) + 1)@ exceeds @maxBound::Int@,
 either of the following usually happens:

 1) @variant m@ for large m is computed. Because the cost is O(m), it
 requests great time and space.

 2) @variant m@ for negative m is requested, and that causes the "(!!):
 negative index" error.

 == Solution: ==
 @variant m@ could be computed in O(log m), for example,
 {{{
 logvariant :: Integral i => i -> Gen a -> Gen a
 logvariant 0 = variant 0
 logvariant n | n > 0 = logvariant (n `div` 2) . variant (fromIntegral (n
 `mod` 2)) . variant 1
              | otherwise = error "logvariant: negative argument"
 }}}
 Also, negative arguments should be dealt with correctly, thus:
 {{{
 newvariant :: Integral i => i -> Gen a -> Gen a
 newvariant n | n >= 0    = logvariant n      . variant 0
              | otherwise = logvariant (-1-n) . variant 1
 instance Arbitrary Integer where
         arbitrary = ...
         coarbitrary = newvariant
 }}}
 I enclose a patch for doing the above. I minimized the affected parts, but
 coarbitrary for Int could also be replaced with @newvariant at . (Or even the
 definition of @variant@ could be replaced.)

-- 
Ticket URL: <http://hackage.haskell.org/trac/ghc/ticket/1026>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler


More information about the Glasgow-haskell-bugs mailing list