Поиск…


Вступление

Числа с плавающей запятой - это числа, которые имеют дробные части (обычно выраженные с десятичной точкой). В Java существует два примитивных типа для чисел с плавающей запятой, которые являются float (использует 4 байта) и double (использует 8 байтов). Эта страница документации предназначена для подробного описания операций с примерами, которые могут выполняться с плавающей точкой в ​​Java.

Сравнение значений с плавающей запятой

Вы должны быть осторожны при сравнении значений с плавающей запятой ( float или double ) с использованием реляционных операторов: == != , < И т. Д. Эти операторы дают результаты в соответствии с двоичными представлениями значений с плавающей запятой. Например:

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

Расчет oneThird ввел крошечную ошибку округления, и когда мы умножаем oneThird на 3 мы получаем результат, немного отличающийся от 1.0 .

Эта проблема неточных представлений более резкая, когда мы пытаемся смешивать double и float в вычислениях. Например:

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

Представления с плавающей запятой, используемые в Java для float и double типов, имеют ограниченное количество цифр точности. Для типа float точность составляет 23 двоичных цифры или около 8 десятичных цифр. Для double типа это 52 бит или около 15 десятичных цифр. Кроме того, некоторые арифметические операции будут приводить к ошибкам округления. Поэтому, когда программа сравнивает значения с плавающей запятой, стандартная практика определяет приемлемую дельта для сравнения. Если разница между двумя числами меньше дельта, они считаются равными. Например

if (Math.abs(v1 - v2) < delta)

Пример сравнения 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();
        }
    }
}

Результат:

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

Также для сравнения double и float примитивных типов может использоваться статический метод compare соответствующего типа бокса. Например:

double a = 1.0;
double b = 1.0001;

System.out.println(Double.compare(a, b));//-1
System.out.println(Double.compare(b, a));//1

Наконец, определение того, какие дельта наиболее подходят для сравнения, может быть сложным. Общепринятый подход состоит в том, чтобы выбрать значения дельты, о которых говорят наши интуиции. Однако, если вы знаете масштаб и (истинную) точность входных значений и выполненные вычисления, может быть возможно получить математически обоснованные оценки точности результатов и, следовательно, для дельт. (Существует формальная ветвь математики, известная как численный анализ, которая раньше преподавалась ученым-вычислителям, которые занимались этим анализом).

OverFlow и UnderFlow

Тип данных с плавающей запятой

Тип данных float представляет собой 32-битную 32-битную IEEE 754 с плавающей точкой.

Float переполнение

Максимально возможное значение равно 3.4028235e+38 Когда оно превышает это значение, оно дает Infinity

float f = 3.4e38f;
float result = f*2;        
System.out.println(result); //Infinity

Float UnderFlow

Минимальное значение - 1.4e-45f, когда оно меньше этого значения, оно производит 0.0

    float f = 1e-45f;
    float result = f/1000;
    System.out.println(result);

двойной тип данных

Двойным типом данных является 64-bit плавающая точка IEEE 754 с двойной точностью.

Double OverFlow

Максимальное возможное значение: 1.7976931348623157e+308 Когда оно превышает это значение, оно создает Infinity

double d = 1e308;
double result=d*2;      
System.out.println(result); //Infinity

Double UnderFlow

Минимальное значение - 4.9e-324, когда оно меньше этого значения, оно производит 0.0

    double d = 4.8e-323;
    double result = d/1000;
    System.out.println(result); //0.0

Форматирование значений с плавающей запятой

Числа с плавающей запятой могут быть отформатированы как десятичное число, используя String.format с флагом '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"

Числа с плавающей запятой могут быть отформатированы как десятичное число с использованием 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

Строгое соответствие спецификации IEEE

По умолчанию операции с плавающей запятой по float и double строго не соответствуют правилам спецификации IEEE 754. Выражению разрешено использовать расширения, специфичные для реализации, для диапазона этих значений; что позволяет им быть более точными, чем требуется.

strictfp отключает это поведение. Он применяется к классу, интерфейсу или методу и относится ко всему содержащемуся в нем, например к классам, интерфейсам, методам, конструкторам, инициализаторам переменных и т. Д. С strictfp промежуточные значения выражения с плавающей запятой должны быть в пределах установленное значение float или заданное двойное значение. Это приводит к тому, что результаты таких выражений будут точно такими, которые предсказывают спецификация IEEE 754.

Все константные выражения неявно строги, даже если они не входят в область strictfp .

Таким образом, strictfp имеет чистый эффект, иногда делая вычисления с strictfp углом зрения менее точными, а также может делать операции с плавающей точкой медленнее (поскольку ЦП теперь делает больше работы, чтобы гарантировать, что какая-либо собственная дополнительная точность не влияет на результат). Однако это также приводит к тому, что результаты будут одинаковыми на всех платформах. Поэтому он полезен в таких вещах, как научные программы, где воспроизводимость важнее скорости.

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
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow