Nếu bạn đang suy nghĩ về việc sử dụng dấu phẩy động để trợ giúp với số nguyên học, bạn phải cẩn thận.
Tôi thường cố gắng tránh tính toán FP bất cứ khi nào có thể.
Hoạt động dấu phẩy động không chính xác. Bạn không bao giờ có thể biết chắc chắn những gì sẽ (int)(Math.log(65536)/Math.log(2))
đánh giá. Ví dụ: Math.ceil(Math.log(1<<29) / Math.log(2))
là 30 trên PC của tôi về mặt toán học, nó phải chính xác là 29. Tôi không tìm thấy giá trị cho x khi (int)(Math.log(x)/Math.log(2))
thất bại (chỉ vì chỉ có 32 giá trị "nguy hiểm"), nhưng điều đó không có nghĩa là nó sẽ hoạt động cùng một cách trên bất kỳ PC nào.
Thủ thuật thông thường ở đây là sử dụng "epsilon" khi làm tròn. Giống như (int)(Math.log(x)/Math.log(2)+1e-10)
không bao giờ nên thất bại. Sự lựa chọn của "epsilon" này không phải là một nhiệm vụ tầm thường.
Trình diễn nhiều hơn, sử dụng một nhiệm vụ tổng quát hơn - cố gắng thực hiện int log(int x, int base)
:
Mã kiểm tra:
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);
}
}
}
Nếu chúng ta sử dụng logarit đơn giản nhất,
static int log(int x, int base)
{
return (int) (Math.log(x) / Math.log(base));
}
bản in này:
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
...
Để hoàn toàn thoát khỏi lỗi, tôi đã phải thêm epsilon trong khoảng từ 1e-11 đến 1e-14. Bạn có thể nói điều này trước khi thử nghiệm? Tôi chắc chắn không thể.