nick8325 / quickcheck

Automatic testing of Haskell programs.
Other
713 stars 119 forks source link

shrinkRealFrac & shrinkDecimal problems #349

Closed peterwicksstringfield closed 1 year ago

peterwicksstringfield commented 1 year ago

It looks like shrinkRealFrac can't shrink negative numbers and goes into an infinite loop on positive infinity. shrinkDecimal handles negatives okay but has the same problem on positive infinity.

Prelude Test.QuickCheck> shrinkRealFrac (-17)
[0.0]
Prelude Test.QuickCheck> shrinkRealFrac 17
[0.0,9.0,13.0,15.0,16.0,0.0,9.0,13.0,15.0,16.0]
Prelude Test.QuickCheck> 1/0 :: Float
Infinity
Prelude Test.QuickCheck> shrinkRealFrac (1/0 :: Float)
[0.0,0.0,0.0,0.0,0.0,^C,0.0,0.0,0Interrupted.
Prelude Test.QuickCheck> shrinkDecimal (1/0 :: Float)
[0.0,0.0,0.0,0.0,0.0,0.0,^C0.0,0.Interrupted.

This is with

$ nix-shell -p "haskell.packages.ghc8107.ghcWithPackages (pkgs: with pkgs; [ QuickCheck ] )"
$ ghci --version
The Glorious Glasgow Haskell Compilation System, version 8.10.7
$ cabal info QuickCheck
[...]
    Versions installed: 2.14.2
[...]

Looks to me like the problem is:

  1. That the infinity-branch for shrinkRealFrac is overlaying the negative-number-branch; since (2*x + 1 > x) is false for negative numbers.
  2. The "iterate (*2) 0" in both functions is a mistake?

Test/QuickCheck/Arbitrary.hs line 1221

shrinkRealFrac :: RealFrac a => a -> [a]
shrinkRealFrac x
  | not (x == x)  = 0 : take 10 (iterate (*2) 0) -- NaN
  | not (2*x+1>x) = 0 : takeWhile (<x) (iterate (*2) 0) -- infinity
  | x < 0 = negate x:map negate (shrinkRealFrac (negate x))
  | otherwise = [...]
shrinkDecimal :: RealFrac a => a -> [a]
shrinkDecimal x
  | not (x == x)  = 0 : take 10 (iterate (*2) 0)        -- NaN
  | not (2*abs x+1>abs x) = 0 : takeWhile (<x) (iterate (*2) 0) -- infinity
  | otherwise = [...]
nick8325 commented 1 year ago

Thanks for the bug report! You're right: that code is obviously nonsense. I can only guess I meant to do iterate (*2) 1 in the first two cases, and the infinity test should look at the absolute value of x.