Java Language
Java-rörelsepunktsoperationer
Sök…
Introduktion
Flyttalsnummer är siffror som har bråkdelar (vanligtvis uttryckta med en decimal). I Java finns det två primitiva typer för flytpunktsnummer som är float
(använder 4 byte) och double
(använder 8 byte). Denna dokumentationssida är avsedd för detaljer med exempel på operationer som kan göras på flytande punkter i Java.
Jämförelse av flytande punktvärden
Du bör vara försiktig när du jämför värden på flytande punkt ( float
eller double
) med hjälp av relationella operatörer: ==
!=
, <
Och så vidare. Dessa operatörer ger resultat enligt de binära representationerna för de flytande punktvärdena. Till exempel:
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"
}
}
Beräkningen oneThird
har infört ett litet avrundningsfel, och när vi multiplicerar oneThird
med 3
vi ett resultat som är något annorlunda än 1.0
.
Problemet med inexakta representationer är mer tydligt när vi försöker blanda double
och float
i beräkningar. Till exempel:
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
}
}
Flytande punktrepresentationer som används i Java för float
och double
typer har begränsat antal siffror med precision. För float
är precisionen 23 binära siffror eller cirka 8 decimaler. För den double
typen är det 52 bitar eller cirka 15 decimaler. Dessutom kommer vissa aritmetiska operationer att införa avrundningsfel. Därför, när ett program jämför flytande punktvärden, är det standardpraxis att definiera ett acceptabelt delta för jämförelsen. Om skillnaden mellan de två siffrorna är mindre än deltaet, bedöms de vara lika. Till exempel
if (Math.abs(v1 - v2) < delta)
Delta jämför exempel:
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();
}
}
}
Resultat:
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
För jämförelse av double
och float
primitiva typer kan även statisk compare
av motsvarande boxningstyp användas. Till exempel:
double a = 1.0;
double b = 1.0001;
System.out.println(Double.compare(a, b));//-1
System.out.println(Double.compare(b, a));//1
Slutligen kan det vara svårt att bestämma vilka deltor som är mest lämpliga för en jämförelse. En vanlig metod är att välja deltavärden som vår intuition säger handlar om. Men om du känner till skala och (sann) noggrannhet för ingångsvärdena och beräkningarna som utförts, kan det vara möjligt att få fram matematiska ljudgränser på resultatens noggrannhet och därmed för deltorna. (Det finns en formell gren av matematik känd som numerisk analys som brukade läras till beräkningsforskare som täckte denna typ av analys.)
OverFlow och UnderFlow
Flottatatyp
Flottatatypen är en enda precision 32-bitars IEEE 754 flytande punkt.
Float
över
Maximalt möjligt värde är 3.4028235e+38
, När det överskrider detta värde producerar det Infinity
float f = 3.4e38f;
float result = f*2;
System.out.println(result); //Infinity
Float
UnderFlow
Minsta värde är 1,4e-45f, när det är under detta värde ger det 0.0
float f = 1e-45f;
float result = f/1000;
System.out.println(result);
dubbel datatyp
Den dubbla datatypen är en dubbelprecision 64-bit
IEEE 754 flytande punkt.
Double
överflöde
Maximalt möjligt värde är 1.7976931348623157e+308
, När det överskrider detta värde producerar det Infinity
double d = 1e308;
double result=d*2;
System.out.println(result); //Infinity
Double
UnderFlow
Minsta värde är 4,9e-324, när det är under detta värde ger det 0.0
double d = 4.8e-323;
double result = d/1000;
System.out.println(result); //0.0
Formatera värdena för flytande punkt
Flyttalsnummer kan formateras som ett decimaltal med String.format
med 'f'
-flaggan
//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"
Flyttalsnummer kan formateras som ett decimaltal med 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
Strikt efterlevnad av IEEE-specifikationen
Som standard följer flyttalsoperationer på float
och double
inte strikt reglerna i IEEE 754-specifikationen. Ett uttryck får använda implementeringsspecifika tillägg till området för dessa värden; väsentligen tillåter dem att vara mer exakta än krävs.
strictfp
inaktiverar detta beteende. Den tillämpas på en klass, gränssnitt, eller metod, och gäller allt som finns i den, såsom klasser, gränssnitt, metoder, konstruktörer, variabla initializers etc. Med strictfp
, måste mellanvärden på ett flyttals uttryck vara inom floatvärdeuppsättningen eller dubbelvärdesuppsättningen. Detta gör att resultaten av sådana uttryck är exakt de som IEEE 754-specifikationen förutspår.
Alla ständiga uttryck är implicit strikt, även om de inte ligger inom ett strictfp
område.
Därför har strictfp
nettoeffekten att ibland göra vissa hörnfallsberäkningar mindre exakta, och kan också göra flytande punktåtgärder långsammare (eftersom CPU nu gör mer arbete för att säkerställa att någon infödd extra precision inte påverkar resultatet). Men det gör också att resultaten är exakt desamma på alla plattformar. Det är därför användbart i saker som vetenskapliga program, där reproducerbarheten är viktigare än hastighet.
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;
}
}
}