Я пытаюсь получить размер в байтах большого целого числа, учитывая, что под капотом есть математика произвольной точности, и я хотел бы знать, сколько места фактически использует результат.

Вот пример:

Prelude> import Data.Bits
Prelude> let fac1000 = product [1..1000] # big!
Prelude Data.Bits> finiteBitSize fac1000

<interactive>:37:1: error:
    • Ambiguous type variable ‘b0’ arising from a use of ‘finiteBitSize’
      prevents the constraint ‘(FiniteBits b0)’ from being solved.
      Probable fix: use a type annotation to specify what ‘b0’ should be.
      These potential instances exist:
        instance FiniteBits Bool -- Defined in ‘Data.Bits’
        instance FiniteBits Int -- Defined in ‘Data.Bits’
        instance FiniteBits Word -- Defined in ‘Data.Bits’
    • In the expression: finiteBitSize fac1000
      In an equation for ‘it’: it = finiteBitSize fac1000

<interactive>:37:15: error:
    • Ambiguous type variable ‘b0’ arising from a use of ‘fac1000’
      prevents the constraint ‘(Num b0)’ from being solved.
      Probable fix: use a type annotation to specify what ‘b0’ should be.
      These potential instances exist:
        instance Num Integer -- Defined in ‘GHC.Num’
        instance Num Double -- Defined in ‘GHC.Float’
        instance Num Float -- Defined in ‘GHC.Float’
        ...plus two others
        (use -fprint-potential-instances to see them all)
    • In the first argument of ‘finiteBitSize’, namely ‘fac1000’
      In the expression: finiteBitSize fac1000
      In an equation for ‘it’: it = finiteBitSize fac1000

Предлагаемые ими аннотации типов не кажутся разумными, когда я приведу целое число, например:

Prelude Data.Bits> finiteBitSize (fac1000 :: Int)
64

Ну, это большое число, и я не верю этому. В питоне я получаю:

>>> import sys, math
>>> sys.getsizeof(math.factorial(1000))
1164

Который выглядит намного более вероятным для астрономически большого 4.02e2568.

5
Mittenchops 28 Май 2019 в 02:01

2 ответа

Лучший ответ

Вы можете приблизить количество байтов, запросив его базу 256 журналов, используя пакет целочисленных логарифмов:

Math.NumberTheory.Logarithms> integerLogBase 256 (product [1..1000])
1066

Это только приблизительное значение, поскольку оно учитывает только байты, используемые для хранения числа; как правило, целые числа произвольной точности также имеют некоторые издержки для хранения информации о длине числа и, возможно, небольшого перераспределения, и ни одно из них не будет учтено при взятии логарифма.

Если вы не против получить приблизительный размер в битах, а не в байтах, integerLog2 будет быстрее.

Math.NumberTheory.Logarithms> integerLog2 (product [1..1000])
8529

Если вы хотите получить верный ответ, вам нужно прикоснуться к некоторым действительно низкоуровневым API и зависеть от точного определение Integer:

{-# LANGUAGE MagicHash #-}
import Data.Bits
import GHC.Exts
import GHC.Integer.GMP.Internals
import GHC.Prim

sizeOfInteger :: Integer -> Int
sizeOfInteger n = constructorSize + case n of
    S# i -> finiteBitSize (I# i) `div` 8
    Jp# bn -> sizeOfBigNat bn
    Jn# bn -> sizeOfBigNat bn
    where
    constructorSize = finiteBitSize (0 :: Word) `div` 8
    sizeOfBigNat (BN# arr) = constructorSize + I# (sizeofByteArray# arr)

Попробуйте это в ghci:

> sizeOfInteger (product [1..1000])
1088

Осторожно! Я не знаю всех обещаний этих внутренних API. Возможно, что вычисление равных Integer s по-разному может привести к значениям, которые представлены по-разному. Касаясь этих внутренних API, вы иногда теряете гарантии абстрактного внешнего API; в этом случае у вас может не быть того, что x == y подразумевает sizeOfInteger x == sizeOfInteger y. Внимательно прочитайте документы, если вы планируете идти по этому маршруту!

13
Thomas M. DuBuisson 28 Май 2019 в 14:24

Вы неправильно поняли, что {{X0 }} делает. Из документов, акцент мой:

Вернуть количество бит в типе аргумента. Фактическое значение аргумента игнорируется.

Функция finiteBitSize :: FiniteBits b => b -> Int сообщает вам свойство type b и использует аргумент для выбора типа. Любой Int даст вам такой же ответ:

ghci> finiteBitSize (0 :: Int)
64
ghci> finiteBitSize (maxBound :: Int)
64
ghci> finiteBitSize (undefined :: Int)
64

Это потому, что Int - это тип ленивых целых чисел machine , которые помещаются в одно слово. На самом деле:

ghci> product [1..1000] :: Int
0

Чуть меньше, чем вы могли ожидать :-)

Если вы хотите измерить размер product [1..1000] как неограниченный Integer, вам понадобится другой метод. Ответ Даниэля Вагнера предоставляет два хороших подхода, оба математических (как вычислить журнал 2 100!) и GHC-внутренний.

3
Antal Spector-Zabusky 28 Май 2019 в 16:34