[英]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^x
和5 * 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_TWO
是Math.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.