Search code examples
haskelllogarithmrounding-error

Haskell logbase error


I am trying to calculate the length of an Integer in Haskell, using the fact that the length is equal to truncate (log10(x)+1).

Using Integers I created:

len :: Integer -> Integer
len i = toInteger (truncate (logBase 10 (fromIntegral i)) + 1)

Unfortunately, not all numbers get the correct length. I tried a few different cases and found that:

logBase 10 10         = 1.0
logBase 10 100        = 2.0
logBase 10 1000       = 2.9999..6
logBase 10 10000      = 4.0
logBase 10 100000     = 5.0
logBase 10 1000000    = 5.9999999

Is there a reason why logBase 10 1000 doesn't return 3.0? How do I get the correct log-value for 1000 in base 10?


Solution

  • There is an integer log base function in GHC modules which has type Integer -> Integer -> Int#.

    Example usage:

    {-# LANGUAGE MagicHash #-}
    
    import Control.Monad
    import GHC.Integer.Logarithms ( integerLogBase# )
    import GHC.Exts (Int(..))
    
    main = do
      forM_ [(1::Int)..20] $ \n -> do
        let a = 10^n-1
            la = I# (integerLogBase# 10 a)
            b = 10^n
            lb = I# (integerLogBase# 10 b)
        putStrLn $ show a ++ " -> " ++ show la
        putStrLn $ show b ++ " -> " ++ show lb
    

    Output:

    9 -> 0
    10 -> 1
    99 -> 1
    100 -> 2
    999 -> 2
    1000 -> 3
    9999 -> 3
    10000 -> 4
    99999 -> 4
    100000 -> 5
    999999 -> 5
    1000000 -> 6
    9999999 -> 6
    ...
    9999999999999999999 -> 18
    10000000000000000000 -> 19
    99999999999999999999 -> 19
    100000000000000000000 -> 20