簡體   English   中英

關於Java的BigInteger的toString的問題

[英]Question about Java's BigInteger's toString

Java 的 BigInteger 的 toString 使用遞歸算法,

toString(BigInteger u, StringBuilder sb,
                                 int radix, int digits)

它將 u 划分為 radix、radix^2、radix^4、radix^8(基本上是 radix^2^n)的范圍,

int b = u.bitLength();
        int n = (int) Math.round(Math.log(b * LOG_TWO / logCache[radix]) /
                                 LOG_TWO - 1.0);

然后決定你屬於哪個范圍並使用 divideAndRemainder 連接結果(這是一個遞歸算法)

BigInteger v = getRadixConversionCache(radix, n);
        BigInteger[] results;
        results = u.divideAndRemainder(v);

        int expectedDigits = 1 << n;

        // Now recursively build the two halves of each number.
        toString(results[0], sb, radix, digits - expectedDigits);
        toString(results[1], sb, radix, expectedDigits);

令我困惑的是,為什么它使用

Math.round(Math.log(b * LOG_TWO / logCache[radix]) /
                                 LOG_TWO - 1.0)

我認為它應該使用地板,並且沒有 - 1.0

Math.floor(Math.log(b * LOG_TWO / logCache[radix]) /
                                 LOG_TWO)

為了找到合適的基數^2^n來划分,我也從2到1024進行測試(2^1到2^10)

[2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]

如果使用

Math.round(Math.log(b * LOG_TWO / logCache[radix]) /
                                 LOG_TWO - 1.0)

然后它變成

[-3, -2, -1, -1, 0, 0, 0, 0, 0, 1]

這似乎是錯誤的,但使用

Math.floor(Math.log(b * LOG_TWO / logCache[radix]) /
                                 LOG_TWO)

結果是

[-2, -1, -1, 0, 0, 0, 1, 1, 1, 1]

這似乎是正確的

該代碼的目的是什么?

目的是找到一個BigInteger v ,它的形式為radix^x ,它將BigInteger u分成兩個部分,它們的字符串表示長度大致相同。 “完美”的值可能是 10^x,其中u.sqrt()介於0.5 * 10^x5 * 10^x之間(對於基數 10)。 然而,這樣的計算將是非常低效的。

相反,代碼通過使用來自序列 radix^1、radix^2、radix^4 的值來近似那個“完美”值,因為這些 radix^(2^x) 值很容易計算並且仍然給出足夠好的結果。

讓我們看看該計算如何適用於位長為 1024 的BigInteger (其字符串表示長度為 308 到 309 個字符)。

表達式(int) Math.round(Math.log(b * LOG_TWO / logCache[radix]) / LOG_TWO - 1.0)是包含在一個表達式中的兩個計算:

  • b * LOG_TWO / logCache[radix]計算近似位數以將BigInteger值表示為 radix radix中的字符串

    注意logCache[radix]是用Math.log(radix)初始化的, LOG_TWOMath.log(2) ,所以完整的表達式是b * Math.log(2) / Math.log(radix)

    對於 1024 的位長,此計算得出bitlength (四舍五入為 4 位)。

    讓我們將此中間結果命名為num_digits

  • 然后第二個計算是(int) Math.round(Math.log(num_digits) / Math.log(2) - 1.0) 對於 308.2547 的num_digits ,這給出了 7。

    因此,在我們的示例中,代碼將選擇10^(2^7) (即 10^128)作為除數來拆分原始數字。

為什么它使用Math.round(x-1)而不是Math.floor(x)

請注意,此示例使用非常小的位bitlength來證明這一點。 實際上,這種計算永遠不會用這么小的值完成

它為n提供了更好的值。

以 14 位長度為例( BigInteger值介於 8192 和 16383 之間)。

使用Math.round(Math.log(b * LOG_TWO / logCache[radix]) / LOG_TWO - 1.0)給出 n=1,這意味着代碼將選擇10^(2^1) (即 100)來拆分BigInteger .

使用Math.floor(Math.log(b * LOG_TWO / logCache[radix]) / LOG_TWO)給出 n =2,這意味着代碼將選擇10^(2^2) (即 10000)來拆分BigInteger - 但是對於 8192 的BigInteger值,這意味着它根本不會拆分它。

附加說明

此計算不適用於非常小的位長值。

在基數 10 的情況下,它不適用於小於 4 的位長 - 但從 1 到 3 的位長值意味着BigInteger中的實際值在 1 到 7 的范圍內 - 對於如此小的值,算法不會t 有意義(因為結果僅限於一位數)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM