2014-11-10 30 views
7

Tôi đang cố gắng tính toán độ dài của một Số nguyên trong Haskell, bằng cách sử dụng thực tế là độ dài bằng truncate (log10(x)+1).Lỗi cơ sở dữ liệu Haskell

Sử dụng Số nguyên tôi đã tạo:

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

Thật không may, không phải tất cả các số có độ dài chính xác. Tôi đã thử một vài trường hợp khác nhau và nhận thấy rằng:

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 

Có lý do nào tại sao logBase 10 1000 không trả lại 3.0? Làm cách nào để nhận được giá trị nhật ký chính xác cho 1000 trong cơ sở 10?

+0

'logBase' được định nghĩa là 'log y/log x'; phân chia có lẽ là thủ phạm, trong khi 'log' sẽ đúng (đối với vòng tròn), sự phân chia chúng không nhất thiết phải là. –

+0

@BartekBanachewicz Vì vậy, không có cách nào để có được kết quả chính xác? Đã thử sử dụng (log 1000)/(log 10), nhưng vẫn là 2.99996. Đoán rằng các giá trị log có thể được làm tròn xuống 1000 và tăng lên 10, do đó nó sẽ nhỏ hơn 3. – Pphoenix

+1

Giả sử 'log' là không cần thiết, bạn có thể tránh hoạt động dấu phẩy động liên tục chia cho 10 (và công suất cao hơn) của 10). – kennytm

Trả lời

4

Có chức năng cơ sở nhật ký số nguyên trong các mô-đun GHC có loại Integer -> Integer -> Int#.

Ví dụ sử dụng:

{-# 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 
1

Nếu bạn không cần Double chính xác Floating sau đó sử dụng Float loại thay vào đó và nó có vẻ tốt đẹp. Chẳng hạn như logBase 10 (1000 :: Float) sẽ trả lại 3.0 hoặc chức năng sẽ làm tương tự.

Theo mã của bạn toInteger có vẻ dư thừa kể từ truncate :: (Integral b, RealFrac a) => a -> b đã thực hiện công việc đó. Vì vậy, chỉ đơn giản là có thể làm như

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

này sẽ làm việc một cách chính xác cho đến 9999987.

Các vấn đề liên quan