Java Language
Java Floating Point Operations
Zoeken…
Invoering
Drijvende-kommagetallen zijn getallen met fractionele delen (meestal uitgedrukt met een decimaalteken). In Java, is er twee primitieve types voor floating-point getallen die float
(bestedingen 4 bytes) en double
(bestedingen 8 bytes). Deze documentatiepagina is bedoeld voor details met voorbeelden van bewerkingen die kunnen worden uitgevoerd op drijvende punten in Java.
Drijvende-kommawaarden vergelijken
Je moet voorzichtig zijn bij het vergelijken van drijvende-kommawaarden ( float
of double
) met behulp van relationele operatoren: ==
!=
, <
Enzovoort. Deze operatoren geven resultaten volgens de binaire representaties van de drijvende-kommawaarden. Bijvoorbeeld:
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"
}
}
De berekening oneThird
heeft een kleine afrondingsfout geïntroduceerd en wanneer we oneThird
met 3
vermenigvuldigen, oneThird
we een resultaat dat iets anders is dan 1.0
.
Dit probleem van onnauwkeurige representaties is ernstiger wanneer we proberen double
en float
te float
in berekeningen. Bijvoorbeeld:
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
}
}
De drijvende-komma-representaties die in Java worden gebruikt voor de float
en double
types hebben een beperkt aantal precisiecijfers. Voor het float
type is de precisie 23 binaire cijfers of ongeveer 8 decimale cijfers. Voor het double
type is dit 52 bits of ongeveer 15 decimale cijfers. Bovendien zullen sommige rekenkundige bewerkingen afrondingsfouten veroorzaken. Wanneer een programma drijvende-kommawaarden vergelijkt, is het daarom standaard om een acceptabele delta te definiëren voor de vergelijking. Als het verschil tussen de twee getallen kleiner is dan de delta, worden ze gelijk geacht. Bijvoorbeeld
if (Math.abs(v1 - v2) < delta)
Delta vergelijkingsvoorbeeld:
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();
}
}
}
Resultaat:
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
Ook voor de vergelijking van double
en float
primitieve typen kan een statische compare
van het overeenkomstige bokstype worden gebruikt. Bijvoorbeeld:
double a = 1.0;
double b = 1.0001;
System.out.println(Double.compare(a, b));//-1
System.out.println(Double.compare(b, a));//1
Ten slotte kan het lastig zijn om te bepalen welke delta's het meest geschikt zijn voor een vergelijking. Een veelgebruikte aanpak is om deltawaarden te kiezen waarvan onze intuïtie zegt dat ze ongeveer kloppen. Als u echter de schaal en (ware) nauwkeurigheid van de invoerwaarden en de uitgevoerde berekeningen kent, is het misschien mogelijk om wiskundig goede grenzen te stellen aan de nauwkeurigheid van de resultaten, en dus aan de delta's. (Er is een formele tak van wiskunde bekend als numerieke analyse die vroeger aan computationele wetenschappers werd geleerd die dit soort analyses omvatte.)
OverFlow en UnderFlow
Float- gegevenstype
Het float-gegevenstype is een 32-bits IEEE 754-drijvende komma met enkele precisie.
Overloop Float
De maximaal mogelijke waarde is 3.4028235e+38
Wanneer deze waarde wordt overschreden, wordt Infinity
geproduceerd
float f = 3.4e38f;
float result = f*2;
System.out.println(result); //Infinity
Float
UnderFlow
Minimale waarde is 1.4e-45f, wanneer deze onder deze waarde komt, produceert deze 0.0
float f = 1e-45f;
float result = f/1000;
System.out.println(result);
dubbel gegevenstype
Het dubbele gegevenstype is een 64-bit
IEEE 754-drijvende-komma met dubbele precisie.
Double
overloop
De maximale mogelijke waarde is 1.7976931348623157e+308
Wanneer deze waarde wordt overschreden, produceert deze Infinity
double d = 1e308;
double result=d*2;
System.out.println(result); //Infinity
Double
onderstroom
Minimale waarde is 4.9e-324, wanneer deze onder deze waarde komt, produceert deze 0.0
double d = 4.8e-323;
double result = d/1000;
System.out.println(result); //0.0
De waarden voor zwevende punten opmaken
Drijvende komma Getallen kunnen worden opgemaakt als een decimaal getal met String.format
met 'f'
vlag '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"
Zwevende punt Nummers kunnen worden opgemaakt als een decimaal getal met behulp van 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
Strikte naleving van de IEEE-specificatie
Drijvende-kommabewerkingen op float
en double
voldoen standaard niet strikt aan de regels van de IEEE 754-specificatie. Een uitdrukking mag implementatiespecifieke uitbreidingen van het bereik van deze waarden gebruiken; in wezen waardoor ze nauwkeuriger dan nodig zijn.
strictfp
schakelt dit gedrag uit. Deze wordt toegepast op een klasse, interface of werkwijze, en geldt voor alles daarin opgenomen, zoals klassen, interfaces, methoden, constructors variabele initialiseerders, etc. Met strictfp
moeten de tussenwaarden van een floating-point expressie binnen de vlotterwaarde ingesteld of de dubbele waarde ingesteld. Dit zorgt ervoor dat de resultaten van dergelijke expressies precies die zijn die de IEEE 754-specificatie voorspelt.
Alle constante uitdrukkingen zijn impliciet streng, zelfs als ze niet binnen een strictfp
bereik vallen.
Daarom heeft strictfp
het netto-effect dat het soms bepaalde strictfp
minder nauwkeurig maakt, en kan het ook drijvende-kommabewerkingen langzamer maken (omdat de CPU nu meer werk doet om ervoor te zorgen dat eventuele native extra precisie het resultaat niet beïnvloedt). Het zorgt er echter ook voor dat de resultaten op alle platforms exact hetzelfde zijn. Het is daarom nuttig in zaken als wetenschappelijke programma's, waar reproduceerbaarheid belangrijker is dan snelheid.
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;
}
}
}