Wenn Sie darüber nachdenken, Gleitkommawerte für die Ganzzahlarithmetik zu verwenden, müssen Sie vorsichtig sein.
Normalerweise versuche ich, FP-Berechnungen nach Möglichkeit zu vermeiden.
Gleitkommaoperationen sind nicht genau. Sie können nie sicher wissen, was zu (int)(Math.log(65536)/Math.log(2))
bewerten ist. Zum Beispiel Math.ceil(Math.log(1<<29) / Math.log(2))
ist 30 auf meinem PC, wo es mathematisch genau 29 sein sollte. Ich habe keinen Wert für x gefunden, wo (int)(Math.log(x)/Math.log(2))
fehlschlägt (nur weil es nur 32 "gefährliche" Werte gibt), aber das bedeutet nicht, dass es funktioniert auf jedem PC genauso.
Der übliche Trick hier ist die Verwendung von "epsilon" beim Runden. Wie (int)(Math.log(x)/Math.log(2)+1e-10)
sollte niemals scheitern. Die Wahl dieses "Epsilons" ist keine triviale Aufgabe.
Mehr Demonstration mit einer allgemeineren Aufgabe - versuchen zu implementieren int log(int x, int base)
:
Der Testcode:
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);
}
}
}
Wenn wir die einfachste Implementierung des Logarithmus verwenden,
static int log(int x, int base)
{
return (int) (Math.log(x) / Math.log(base));
}
dies druckt:
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
...
Um Fehler vollständig zu beseitigen, musste ich epsilon hinzufügen, das zwischen 1e-11 und 1e-14 liegt. Könnten Sie dies vor dem Testen gesagt haben? Ich konnte es definitiv nicht.