Java Language
Operazioni Java Floating Point
Ricerca…
introduzione
I numeri in virgola mobile sono numeri che hanno parti frazionarie (di solito espresse con un punto decimale). In Java, esistono due tipi primitivi per numeri a virgola mobile che sono float
(utilizza 4 byte) e double
(usa 8 byte). Questa pagina di documentazione è per i dettagli con operazioni sugli esempi che possono essere eseguite su punti mobili in Java.
Confronto tra valori in virgola mobile
È necessario prestare attenzione quando si confrontano valori a virgola mobile ( float
o double
) utilizzando operatori relazionali: ==
!=
, <
E così via. Questi operatori danno risultati in base alle rappresentazioni binarie dei valori in virgola mobile. Per esempio:
public class CompareTest {
public static void main(String[] args) {
double oneThird = 1.0 / 3.0;
double one = oneThird * 3;
System.out.println(one == 1.0); // prints "false"
}
}
Il calcolo del oneThird
ha introdotto un piccolo errore di arrotondamento e quando moltiplichiamo il oneThird
per 3
otteniamo un risultato leggermente diverso da 1.0
.
Questo problema di rappresentazioni inesatte è più marcato quando si tenta di combinare il double
e il float
nei calcoli. Per esempio:
public class CompareTest2 {
public static void main(String[] args) {
float floatVal = 0.1f;
double doubleVal = 0.1;
double doubleValCopy = floatVal;
System.out.println(floatVal); // 0.1
System.out.println(doubleVal); // 0.1
System.out.println(doubleValCopy); // 0.10000000149011612
System.out.println(floatVal == doubleVal); // false
System.out.println(doubleVal == doubleValCopy); // false
}
}
Le rappresentazioni in virgola mobile utilizzate in Java per i tipi float
e double
hanno un numero limitato di cifre di precisione. Per il tipo float
, la precisione è di 23 cifre binarie o di circa 8 cifre decimali. Per il double
tipo, è 52 bit o circa 15 cifre decimali. Inoltre, alcune operazioni aritmetiche introdurranno errori di arrotondamento. Pertanto, quando un programma confronta i valori in virgola mobile, pratica standard per definire un delta accettabile per il confronto. Se la differenza tra i due numeri è inferiore al delta, vengono considerati uguali. Per esempio
if (Math.abs(v1 - v2) < delta)
Esempio di confronto Delta:
public class DeltaCompareExample {
private static boolean deltaCompare(double v1, double v2, double delta) {
// return true iff the difference between v1 and v2 is less than delta
return Math.abs(v1 - v2) < delta;
}
public static void main(String[] args) {
double[] doubles = {1.0, 1.0001, 1.0000001, 1.000000001, 1.0000000000001};
double[] deltas = {0.01, 0.00001, 0.0000001, 0.0000000001, 0};
// loop through all of deltas initialized above
for (int j = 0; j < deltas.length; j++) {
double delta = deltas[j];
System.out.println("delta: " + delta);
// loop through all of the doubles initialized above
for (int i = 0; i < doubles.length - 1; i++) {
double d1 = doubles[i];
double d2 = doubles[i + 1];
boolean result = deltaCompare(d1, d2, delta);
System.out.println("" + d1 + " == " + d2 + " ? " + result);
}
System.out.println();
}
}
}
Risultato:
delta: 0.01
1.0 == 1.0001 ? true
1.0001 == 1.0000001 ? true
1.0000001 == 1.000000001 ? true
1.000000001 == 1.0000000000001 ? true
delta: 1.0E-5
1.0 == 1.0001 ? false
1.0001 == 1.0000001 ? false
1.0000001 == 1.000000001 ? true
1.000000001 == 1.0000000000001 ? true
delta: 1.0E-7
1.0 == 1.0001 ? false
1.0001 == 1.0000001 ? false
1.0000001 == 1.000000001 ? true
1.000000001 == 1.0000000000001 ? true
delta: 1.0E-10
1.0 == 1.0001 ? false
1.0001 == 1.0000001 ? false
1.0000001 == 1.000000001 ? false
1.000000001 == 1.0000000000001 ? false
delta: 0.0
1.0 == 1.0001 ? false
1.0001 == 1.0000001 ? false
1.0000001 == 1.000000001 ? false
1.000000001 == 1.0000000000001 ? false
Anche per il confronto dei tipi primitivi a double
e float
possibile utilizzare il metodo di compare
statico del tipo di boxing corrispondente. Per esempio:
double a = 1.0;
double b = 1.0001;
System.out.println(Double.compare(a, b));//-1
System.out.println(Double.compare(b, a));//1
Infine, determinare quali delta sono più appropriati per un confronto può essere complicato. Un approccio comunemente usato è quello di selezionare valori delta che secondo la nostra intuizione sono giusti. Tuttavia, se si conoscono la scala e l'accuratezza (vera) dei valori di input e i calcoli eseguiti, potrebbe essere possibile ottenere limiti matematicamente validi sulla precisione dei risultati, e quindi per i delta. (Esiste un ramo formale della matematica noto come analisi numerica che veniva insegnato agli scienziati computazionali che coprivano questo tipo di analisi).
OverFlow e UnderFlow
Float data type
Il tipo di dati float è un punto mobile IEEE 754 a 32 bit a precisione singola.
Float
overflow
Il valore massimo possibile è 3.4028235e+38
, quando supera questo valore produce Infinity
float f = 3.4e38f;
float result = f*2;
System.out.println(result); //Infinity
Float
UnderFlow
Il valore minimo è 1.4e-45f, quando va sotto questo valore produce 0.0
float f = 1e-45f;
float result = f/1000;
System.out.println(result);
doppio tipo di dati
Il doppio tipo di dati è un punto mobile IEEE 754 a 64-bit
a doppia precisione.
Double
OverFlow
Il valore massimo possibile è 1.7976931348623157e+308
, quando supera questo valore produce Infinity
double d = 1e308;
double result=d*2;
System.out.println(result); //Infinity
Double
UnderFlow
Il valore minimo è 4.9e-324, quando va sotto questo valore produce 0.0
double d = 4.8e-323;
double result = d/1000;
System.out.println(result); //0.0
Formattare i valori in virgola mobile
Virgola mobile I numeri possono essere formattati come numeri decimali utilizzando String.format
con 'f'
flag 'f'
//Two digits in fracttional part are rounded
String format1 = String.format("%.2f", 1.2399);
System.out.println(format1); // "1.24"
// three digits in fractional part are rounded
String format2 = String.format("%.3f", 1.2399);
System.out.println(format2); // "1.240"
//rounded to two digits, filled with zero
String format3 = String.format("%.2f", 1.2);
System.out.println(format3); // returns "1.20"
//rounder to two digits
String format4 = String.format("%.2f", 3.19999);
System.out.println(format4); // "3.20"
Virgola mobile I numeri possono essere formattati come numeri decimali utilizzando DecimalFormat
// rounded with one digit fractional part
String format = new DecimalFormat("0.#").format(4.3200);
System.out.println(format); // 4.3
// rounded with two digit fractional part
String format = new DecimalFormat("0.##").format(1.2323000);
System.out.println(format); //1.23
// formatting floating numbers to decimal number
double dv = 123456789;
System.out.println(dv); // 1.23456789E8
String format = new DecimalFormat("0").format(dv);
System.out.println(format); //123456789
Aderenza rigorosa alle specifiche IEEE
Per impostazione predefinita, le operazioni in virgola mobile su float
e double
non rispettano rigorosamente le regole della specifica IEEE 754. Un'espressione può utilizzare estensioni specifiche dell'implementazione nell'intervallo di questi valori; essenzialmente permettendo loro di essere più precisi del necessario.
strictfp
disabilita questo comportamento. Si applica a una classe, un'interfaccia o un metodo e si applica a tutto ciò che vi è contenuto, come classi, interfacce, metodi, costruttori, inizializzatori variabili, ecc. Con strictfp
, i valori intermedi di un'espressione a virgola mobile devono essere compresi il valore float impostato o il doppio valore impostato. Ciò fa sì che i risultati di tali espressioni siano esattamente quelli previsti dalla specifica IEEE 754.
Tutte le espressioni costanti sono implicitamente rigide, anche se non sono all'interno di uno scope strictfp
.
Di conseguenza, strictfp
ha l'effetto netto di rendere alcuni calcoli caso-angolo meno precisi e può anche rallentare le operazioni in virgola mobile (poiché la CPU sta facendo più lavoro per garantire che qualsiasi precisione extra nativa non influenzi il risultato). Tuttavia, fa sì che i risultati siano esattamente uguali su tutte le piattaforme. È quindi utile in cose come programmi scientifici, in cui la riproducibilità è più importante della velocità.
public class StrictFP { // No strictfp -> default lenient
public strictfp float strict(float input) {
return input * input / 3.4f; // Strictly adheres to the spec.
// May be less accurate and may be slower.
}
public float lenient(float input) {
return input * input / 3.4f; // Can sometimes be more accurate and faster,
// but results may not be reproducable.
}
public static final strictfp class Ops { // strictfp affects all enclosed entities
private StrictOps() {}
public static div(double dividend, double divisor) { // implicitly strictfp
return dividend / divisor;
}
}
}