La prima cosa da capire è che la rappresentazione binaria di 0.15625
non è 0.00101
. Sì, è quello che scriveresti se scrivessi il numero a mano. La rappresentazione effettiva del numero all'interno del computer tramite IEE 754 per la precisione singola a 32 bit. Questo è specificato come parte della specifica della lingua Java 4.2 :
The floating-point types are float
, whose values include the 32-bit IEEE 754 floating-point numbers, and double
, whose values include the 64-bit IEEE 754 floating-point numbers.
La rappresentazione binaria IEEE 754 di 0.15625
è: 0x3E200000
che può essere scomposta in:
- un bit di segno:
0
che indica che è positivo.
- l'esponente per i bit:
01111100
che è 124. Questo valore viene sottratto da 127 per ottenere l'esponente effettivo che verrà utilizzato ( -3
)
- il significato e:
.0100000 00000000 00000000
. Il .
che ho messo lì è per la lettura in modo che sia byte, non parte del valore (il significativo utilizza i 23 bit più bassi del numero ... quindi il 24 ° bit rappresento con un .
quindi l'allineamento è più facile da leggere) Questo valore è aggiunto a 1
dandoci il valore binario di 1.010....
o in base 10, 1.25000...
Combinando tutti questi elementi, otteniamo:
(- 1) 0
* 1.01
2 * 2 -3 che risulta essere, quando scritto,% codice%. Ma ricorda che abbiamo a che fare con 0.00101
.
Consente di acquisire un file sorgente Java rapido:
package com.michaelt.so.floating;
public class Demo {
float foo = 0.15625f;
}
Il codice byte per questo, quando decompilato è:
// class version 50.0 (50)
// access flags 0x21
public class com/michaelt/so/floating/Demo {
// compiled from: Demo.java
// access flags 0x0
F foo
// access flags 0x1
public <init>()V
L0
LINENUMBER 3 L0
ALOAD 0
INVOKESPECIAL java/lang/Object.<init> ()V
L1
LINENUMBER 4 L1
ALOAD 0
LDC 0.15625
PUTFIELD com/michaelt/so/floating/Demo.foo : F
RETURN
L2
LOCALVARIABLE this Lcom/michaelt/so/floating/Demo; L0 L2 0
MAXSTACK = 2
MAXLOCALS = 1
}
Che in realtà non è troppo interessante. Puoi vedere il 0x3E200000
in là che è un caricare l'opcode costante . Prende un argomento da un pool costante e lo spinge sullo stack.
Quindi, guardiamo Demo.class con un visualizzatore di dump esadecimale.
$ hexdump -C Demo.class
00000000 ca fe ba be 00 00 00 32 00 15 0a 00 05 00 11 04 |.......2........|
00000010 3e 20 00 00 09 00 04 00 12 07 00 13 07 00 14 01 |> ..............|
00000020 00 03 66 6f 6f 01 00 01 46 01 00 06 3c 69 6e 69 |..foo...F...<ini|
00000030 74 3e 01 00 03 28 29 56 01 00 04 43 6f 64 65 01 |t>...()V...Code.|
00000040 00 0f 4c 69 6e 65 4e 75 6d 62 65 72 54 61 62 6c |..LineNumberTabl|
00000050 65 01 00 12 4c 6f 63 61 6c 56 61 72 69 61 62 6c |e...LocalVariabl|
00000060 65 54 61 62 6c 65 01 00 04 74 68 69 73 01 00 1f |eTable...this...|
00000070 4c 63 6f 6d 2f 6d 69 63 68 61 65 6c 74 2f 73 6f |Lcom/michaelt/so|
00000080 2f 66 6c 6f 61 74 69 6e 67 2f 44 65 6d 6f 3b 01 |/floating/Demo;.|
00000090 00 0a 53 6f 75 72 63 65 46 69 6c 65 01 00 09 44 |..SourceFile...D|
000000a0 65 6d 6f 2e 6a 61 76 61 0c 00 08 00 09 0c 00 06 |emo.java........|
000000b0 00 07 01 00 1d 63 6f 6d 2f 6d 69 63 68 61 65 6c |.....com/michael|
000000c0 74 2f 73 6f 2f 66 6c 6f 61 74 69 6e 67 2f 44 65 |t/so/floating/De|
000000d0 6d 6f 01 00 10 6a 61 76 61 2f 6c 61 6e 67 2f 4f |mo...java/lang/O|
000000e0 62 6a 65 63 74 00 21 00 04 00 05 00 00 00 01 00 |bject.!.........|
000000f0 00 00 06 00 07 00 00 00 01 00 01 00 08 00 09 00 |................|
00000100 01 00 0a 00 00 00 39 00 02 00 01 00 00 00 0b 2a |......9........*|
00000110 b7 00 01 2a 12 02 b5 00 03 b1 00 00 00 02 00 0b |...*............|
00000120 00 00 00 0a 00 02 00 00 00 03 00 04 00 04 00 0c |................|
00000130 00 00 00 0c 00 01 00 00 00 0b 00 0d 00 0e 00 00 |................|
00000140 00 01 00 0f 00 00 00 02 00 10 |..........|
0000014a
Guarda, proprio lì all'inizio della seconda riga ...
00000010 3e 20 00 00
C'è il numero.
Qualcuno sta per chiedere "ma perché il numero è così alto? Non c'è nemmeno un LDC 0.15625
vicino (il valore esadecimale per 12
). ' E hanno ragione a chiedere questo. Ricorda che lo strumento LDC ha un valore dal pool costante . Il codice byte che avevo in precedenza è stato riconvertito in qualcosa che una persona potrebbe leggere.
La struttura di un file di classe può essere trovata nella specifica Java Virtual Machine sezione 4.1 :
ClassFile {
u4 magic;
u2 minor_version;
u2 major_version;
u2 constant_pool_count;
cp_info constant_pool[constant_pool_count-1];
u2 access_flags;
u2 this_class;
u2 super_class;
u2 interfaces_count;
u2 interfaces[interfaces_count];
u2 fields_count;
field_info fields[fields_count];
u2 methods_count;
method_info methods[methods_count];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
Abbiamo il numero magico in cima ( LDC
), e la versione minore ( ca fe ba be
), e la versione principale ( 00 00
= 50 10 ) e poi il conteggio del pool costante ( 00 32
) che quindi ha il pool costante stesso.
Il pool costante è specificato nella sezione 4.4 che ha il formato:
cp_info {
u1 tag;
u1 info[];
}
Il valore del tag di 00 15
dice che "ciò che è prossimo è un float". Puoi vederlo alla fine della riga precedente.
00000000 ca fe ba be 00 00 00 32 00 15 0a 00 05 00 11 04 |.......2........|
00000010 3e 20 00 00 09 00 04 00 12 07 00 13 07 00 14 01 |> ..............|
Le altre cose attorno al numero sono informazioni sulle posizioni dei metodi (ha ancora un costruttore predefinito), il nome del campo, il nome della classe, ecc ... ci sono un sacco di costanti .
Tuttavia, la cosa fondamentale è che puoi vedere il numero in virgola mobile memorizzato nel file di classe come un valore IEEE 754 che indica che questo è qualcosa che fa il compilatore.