如果你正在考慮使用浮點幫助整數算術,你必須要小心。
我通常儘可能地避免FP計算。
浮點運算不準確。你永遠無法知道(int)(Math.log(65536)/Math.log(2))
會評估什麼。例如,Math.ceil(Math.log(1<<29)/Math.log(2))
在我的PC上爲30,在數學上它應該是29.我沒有找到x的值,其中(int)(Math.log(x)/Math.log(2))
失敗(僅僅因爲只有32個「危險」值),但這並不意味着它會在任何PC上以相同的方式工作。
這裏常用的技巧是在四捨五入時使用「epsilon」。像(int)(Math.log(x)/Math.log(2)+1e-10)
應該永遠不會失敗。這個「epsilon」的選擇不是一項簡單的任務。
更多演示,使用更一般的任務 - 試圖實現int log(int x, int base)
:
的測試代碼:
static int pow(int base, int power) {
int result = 1;
for (int i = 0; i < power; i++)
result *= base;
return result;
}
private static void test(int base, int pow) {
int x = pow(base, pow);
if (pow != log(x, base))
System.out.println(String.format("error at %d^%d", base, pow));
if(pow!=0 && (pow-1) != log(x-1, base))
System.out.println(String.format("error at %d^%d-1", base, pow));
}
public static void main(String[] args) {
for (int base = 2; base < 500; base++) {
int maxPow = (int) (Math.log(Integer.MAX_VALUE)/Math.log(base));
for (int pow = 0; pow <= maxPow; pow++) {
test(base, pow);
}
}
}
如果我們用最直觀的實現對數,
static int log(int x, int base)
{
return (int) (Math.log(x)/Math.log(base));
}
此打印:
error at 3^5
error at 3^10
error at 3^13
error at 3^15
error at 3^17
error at 9^5
error at 10^3
error at 10^6
error at 10^9
error at 11^7
error at 12^7
...
要完全擺脫錯誤,我不得不添加介於1e-11和1e-14之間的epsilon。 您可以在測試之前告訴這個嗎? 我絕對不能。
你怎麼測試這個性能?在我的系統(Core i7,jdk 1.6 x64)上,整數版本幾乎比浮點版本快10倍。一定要用函數的結果做一些事情,以便JIT不能完全刪除計算! – x4u 2010-07-22 03:57:19
你是對的。我沒有使用計算結果,編譯器已經優化了一些東西。現在我的結果和你一樣 - 整數函數速度提高10倍(Core 2 Duo,jdk 1.6 c64) – Nulldevice 2010-07-22 05:03:40
這有效地爲你提供了'Math.floor(Math.log(n)/Math.log(2))',所以它不是真的計算日誌庫2! – Dori 2016-04-20 16:48:46