Haskell logbase错误

10

我正在尝试计算Haskell中整数的长度,使用这样一个事实:长度等于truncate (log10(x)+1)

我使用整数创建了以下内容:

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

不幸的是,并非所有数字都有正确的长度。 我尝试了几种不同情况,发现:

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

为什么logBase 10 1000的结果不是3.0呢?我该如何在以10为底的情况下获得正确的1000的对数值?


1
“logBase”被定义为“log y / log x”; 除法可能是罪魁祸首,因为虽然“log”将是正确的(关于四舍五入),但它们的除法不一定是正确的。 - Bartek Banachewicz
@BartekBanachewicz,所以没有办法得到正确的结果吗?尝试使用(log 1000)/(log 10),但仍然是2.99996。猜测对于1000来说,log值可能会向下舍入,而对于10来说则会向上舍入,因此它将略小于3。 - Pphoenix
2
假设log不是必需的,您可以通过重复除以10(和更高次幂的10)来避免浮点运算。 - kennytm
3
获取正确结果最简单的方法可能是使用numbers包,并调用logBase 10 1000 :: BigFloat Prec50,然后将其舍入为浮点数或双精度浮点数。 这是最简单的方法,但不一定是最好的。 - Bartek Banachewicz
如果您不需要“Double”浮点精度,则可以使用“Float”类型,看起来很好。例如,“logBase 10(1000 :: Float)”将返回“3.0”,或者函数式地,“logBase 10 .(fromInteger :: Integer -> Float)$ 1000”也可以实现相同的效果。 - Redu
4个回答

5
在 GHC 模块中有一个整数对数函数,其类型为 Integer -> Integer -> Int#
使用示例:
{-# 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

输出:

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

1
如果您不需要双精度浮点数,则可以使用Float类型,这样看起来就很好。例如,logBase 10 (1000 :: Float)将返回3.0,或者函数式的logBase 10 . (fromInteger :: Integer -> Float) $ 1000也会做同样的事情。
根据您的代码,toInteger似乎是多余的,因为truncate :: (Integral b, RealFrac a) => a -> b已经完成了该工作。因此,可以简单地这样做。
len :: Integer -> Integer
len = (+1) . truncate . logBase 10 . (fromIntegral :: Integer -> Float)

这将在9999987之前正常工作。

1

你不能只是去吗?

amountOfDigits = length . show

(如果您只对传统十进制数字的数量感兴趣)

对于带有前导零的数字,这将失败。 - Abhijit Sarkar

-1
len :: Integer -> Integer
len x
  | x <= 1 = 1 -- doesn't handle negative numbers
  | otherwise = ceiling (logBase 10 (fromIntegral x :: Float))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接