【问题标题】:Haskell logbase errorHaskell 日志库错误
【发布时间】:2014-11-10 12:43:07
【问题描述】:

我正在尝试使用长度等于truncate (log10(x)+1) 的事实来计算 Haskell 中整数的长度。

使用我创建的整数:

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 的正确对数值?

【问题讨论】:

  • logBase 定义为log y / log x;除法可能是罪魁祸首,虽然log 是正确的(关于四舍五入),但它们的除法不一定是。
  • @BartekBanachewicz 那么有没有办法得到正确的结果?尝试使用 (log 1000) / (log 10),但仍然是 2.99996。猜测 log-values 可能会向下舍入 1000 并向上舍入 10,因此它会略小于 3。
  • 假设log不是必需的,你可以避免反复除以10(以及10的更高幂)的浮点运算。
  • @Pphoenix 获得正确结果的最简单方法是使用numbers 包,然后调用logBase 10 1000 :: BigFloat Prec50,然后将其四舍五入为浮点数或双精度数。最简单,但不一定是最好的。
  • 如果您不需要 Double 浮点精度,那么请改用 Float 类型,它似乎没问题。比如logBase 10 (1000 :: Float) 会返回3.0 或者功能上logBase 10 . (fromInteger :: Integer -> Float) $ 1000 会做同样的事情。

标签: haskell logarithm rounding-error


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    如果您不需要 Double 浮点精度,那么请改用 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 之前正常工作。

    【讨论】:

      【解决方案3】:

      你不能走吗

      amountOfDigits = length . show
      

      (如果您只对传统以 10 为底的位数感兴趣)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-03-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-05-16
        • 2013-03-16
        • 2012-04-17
        相关资源
        最近更新 更多