10

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?

Pphoenix
  • 1,423
  • 1
  • 15
  • 37
  • 1
    `logBase` is defined as `log y / log x`; the division is probably the culprit, as while the `log` will be correct (with regard to roundings), the division of them doesn't have to be. – Bartek Banachewicz Nov 10 '14 at 12:49
  • @BartekBanachewicz So there is no way to get the correct result? Tried using (log 1000) / (log 10), but still 2.99996. Guess that the log-values might be rounded down for 1000 and up for 10, thus it will be slightly smaller than 3. – Pphoenix Nov 10 '14 at 12:53
  • 2
    Suppose `log` is not necessary, you could avoid floating point operation with repeatedly divide by 10 (and higher power of 10). – kennytm Nov 10 '14 at 12:56
  • 3
    @Pphoenix The easiest way to get the correct result could be to use `numbers` package, and call `logBase 10 1000 :: BigFloat Prec50`, then round that into float or double. Easiest, but not necessarily the best. – Bartek Banachewicz Nov 10 '14 at 12:58
  • If you don't need `Double` Floating precision then use `Float` type instead and it seems fine. Such as `logBase 10 (1000 :: Float)` would return `3.0` or functionally `logBase 10 . (fromInteger :: Integer -> Float) $ 1000` would do the same. – Redu Nov 14 '17 at 23:08

4 Answers4

5

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
ErikR
  • 51,541
  • 9
  • 73
  • 124
1

If you don't need Double Floating precision then use Float type instead and it seems fine. Such as logBase 10 (1000 :: Float) would return 3.0 or functionally logBase 10 . (fromInteger :: Integer -> Float) $ 1000 would do the same.

As per your code toInteger seems redundant since truncate :: (Integral b, RealFrac a) => a -> b already does that job. So may simply do like

len :: Integer -> Integer
len = (+1) . truncate . logBase 10 . (fromIntegral :: Integer -> Float)

This will work correctly up until 9999987.

Redu
  • 25,060
  • 6
  • 56
  • 76
1

Can't you just go

amountOfDigits = length . show

(if you only interested in amount of digits in traditional base 10)

-1
len :: Integer -> Integer
len x
  | x <= 1 = 1 -- doesn't handle negative numbers
  | otherwise = ceiling (logBase 10 (fromIntegral x :: Float))
Abhijit Sarkar
  • 21,927
  • 20
  • 110
  • 219