Java Language
Операции с плавающей точкой Java
Поиск…
Вступление
Числа с плавающей запятой - это числа, которые имеют дробные части (обычно выраженные с десятичной точкой). В 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;
}
}
}