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;
        }
    }
}


Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow