2015-11-16 13 views
6

Vor der Suche nach der Methode Long.numberOfLeadingZeros(long i), war ich Casting Longs zu Doppel und Verwendung Math.getExponent(double d). Die Idee war, die doppelte Darstellung des langen zu finden, den Exponenten zu verwenden, um das höchste gesetzte Bit zu erhalten, und es von 64 zu subtrahieren, um die Anzahl der führenden Nullen zu erhalten.Java - führende Nullen in einer langen durch Konvertierung zu Doppel

Dies funktionierten meistens, aber ausgeschaltet war gelegentlich von 1. Die folgenden for-Schleife, das Problem zu markieren, wurde verwendet:

for (int i = 0; i < 64; i++) { 
    double max = Long.MAX_VALUE >>> i; 
    double min = Long.MIN_VALUE >>> i; 
    double neg = -1L >>> i; 
    System.out.format("Max: %-5d Min: %-5d -1: %-5d%n", Math.getExponent(dmax), 
           Math.getExponent(dmin), Math.getExponent(dneg)); 
} 

mit dem wesentlichen Teil des Ausgangs:

... 
Max: 55 Min: 55 -1: 56 
Max: 54 Min: 54 -1: 55 
Max: 52 Min: 53 -1: 54 
Max: 51 Min: 52 -1: 52 
Max: 50 Min: 51 -1: 51 
... 

Die longs mit allen gesetzten Bits sind um 1 über 2^52 ausgeschaltet. As this post explains, Ein Genauigkeitsverlust tritt aufgrund der Speicherung von 53+ signifikanten Bits in der 52-Bit-Mantisse auf. Ich habe jedoch Mühe zu verstehen, warum der Exponent betroffen ist.

Während ich diese Methode nicht mehr benutze, bin ich immer noch neugierig: Warum und unter welchen Umständen versagt diese Methode, die führenden Nullen lange zu finden?

Antwort

3

Die Genauigkeitsgrenze auf double zwingt die Binärdarstellung, auf die nächste Potenz von 2 zu runden, wodurch der Exponent in der Gleitkommadarstellung des Werts double inkrementiert wird. Dies geschieht, weil die Mantisse eines Doppels, einschließlich des implizierten Bits 1, 53 Bits ist, aber ein long 64 Bits hat.

Section 5.1.2 of the JLS bedeckt, was in dieser Erweiterung primitiven Umwandlung passieren kann:

Eine Verbreiterung primitive Umwandlung von int zu schweben, oder von langen zu schweben, oder von langen zu verdoppeln, in Genauigkeitsverlust führen kann -, dass ist, kann das Ergebnis einige der niedrigstwertigen Bits des Wertes verlieren. In diesem Fall ist der resultierende Fließkommawert eine korrekt gerundete Version des Ganzzahlwerts, wobei der Modus IEEE 754 Round-To-Nearest verwendet wird (§4.2.4).

(Hervorhebung von mir)

Hier verwende ich Double.doubleToLongBits die Bits eines double in einem long zu bewahren und Long.toHexString die Hex-Werte der ursprünglichen double s auszudrucken.

System.out.format("Max(%s): %-5d Min(%s): %-5d -1(%s): %-5d%n", 
       Long.toHexString(Double.doubleToLongBits(dmax)), Math.getExponent(dmax), 
       Long.toHexString(Double.doubleToLongBits(dmax)), Math.getExponent(dmin), 
       Long.toHexString(Double.doubleToLongBits(dneg)), Math.getExponent(dneg)); 

Ausgang:

Max(43e0000000000000): 63 Min(43e0000000000000): 63 -1(bff0000000000000): 0  
Max(43d0000000000000): 62 Min(43d0000000000000): 62 -1(43e0000000000000): 63 
Max(43c0000000000000): 61 Min(43c0000000000000): 61 -1(43d0000000000000): 62 
Max(43b0000000000000): 60 Min(43b0000000000000): 60 -1(43c0000000000000): 61 
Max(43a0000000000000): 59 Min(43a0000000000000): 59 -1(43b0000000000000): 60 
Max(4390000000000000): 58 Min(4390000000000000): 58 -1(43a0000000000000): 59 
Max(4380000000000000): 57 Min(4380000000000000): 57 -1(4390000000000000): 58 
Max(4370000000000000): 56 Min(4370000000000000): 56 -1(4380000000000000): 57 
Max(4360000000000000): 55 Min(4360000000000000): 55 -1(4370000000000000): 56 
Max(4350000000000000): 54 Min(4350000000000000): 54 -1(4360000000000000): 55 
Max(433fffffffffffff): 52 Min(433fffffffffffff): 53 -1(4350000000000000): 54 
Max(432ffffffffffffe): 51 Min(432ffffffffffffe): 52 -1(433fffffffffffff): 52 

Die ursprünglichen long Werte mit mehr als 53 1 Bits sind abgerundet, wenn auf eine double umgewandelt, Präzision zu verlieren. Das Exponentenfeld besteht aus den Bits 2 bis 12, sichtbar in den ersten 3 Hexadezimalziffern, die oben ausgedruckt sind.

Wenn der Wert unter 53 1 Bits verschoben wird, double ‚s Präzision jetzt ausreicht, um den Wert ohne Rundung zu halten (das aufrunden nicht mehr erforderlich ist) und der Mantisse des Bits sichtbar wird als‚F‘hex Ziffern.Im Exponentenfeld gibt es eine Diskontinuität von 435 zu 433, die erklärt, warum im Ergebnis von Math.getExponent, von 54 bis 52 eine Diskontinuität vorliegt.

+0

Das macht Sinn. Nach dem Lesen Ihres Beitrags habe ich Werte größer als 2^53 - 1 gezwungen, ihre 11 niedrigstwertigen Bits zu löschen: 'value & = 0xFFFFFFFFFFFFFF700L;' vor der Umwandlung in ein Doppel, was das Runden überflüssig macht. Ich habe auch vergessen, dass die Mantisse das zusätzliche implizierte Bit hat. Danke für die ausführliche Antwort! –

1

Eine Zahl mit allen Einsen, wenn auf weniger Stellen gerundet, wird aufgerundet und wird daher ein bisschen länger. Zum Beispiel (unter der Annahme einer doppelten nur 5 Bits in der Mantisse hat)

111111 becomes 1000000 

wenn gerundet.

Verwandte Themen