Jika Anda berpikir untuk menggunakan floating-point untuk membantu integer arithmetics, Anda harus berhati-hati.
Saya biasanya mencoba menghindari perhitungan FP jika memungkinkan.
Operasi titik-mengambang tidak tepat. Anda tidak akan pernah tahu pasti apa yang akan (int)(Math.log(65536)/Math.log(2))
dievaluasi. Misalnya, Math.ceil(Math.log(1<<29) / Math.log(2))
adalah 30 pada PC saya di mana secara matematis seharusnya 29. Saya tidak menemukan nilai untuk x di mana (int)(Math.log(x)/Math.log(2))
gagal (hanya karena hanya ada 32 nilai "berbahaya"), tetapi itu tidak berarti bahwa ia akan bekerja dengan cara yang sama pada PC apa pun.
Trik yang biasa di sini adalah menggunakan "epsilon" saat pembulatan. Seperti (int)(Math.log(x)/Math.log(2)+1e-10)
seharusnya tidak pernah gagal. Pilihan "epsilon" ini bukanlah tugas sepele.
Lebih banyak demonstrasi, menggunakan tugas yang lebih umum - mencoba menerapkan int log(int x, int base)
:
Kode pengujian:
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);
}
}
}
Jika kita menggunakan implementasi logaritma yang paling mudah,
static int log(int x, int base)
{
return (int) (Math.log(x) / Math.log(base));
}
ini mencetak:
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
...
Untuk benar-benar menghilangkan kesalahan saya harus menambahkan epsilon yaitu antara 1e-11 dan 1e-14. Bisakah Anda memberi tahu ini sebelum pengujian? Saya pasti tidak bisa.