Сохранять точность с двойным в Java

public class doublePrecision { public static void main(String[] args) { double total = 0; total += 5.6; total += 5.8; System.out.println(total); } } 

Вышеприведенный код печатает:

 11.399999999999 

Как мне заставить это просто распечатать (или использовать его как) 11.4?

Как уже упоминалось, вы, вероятно, захотите использовать class BigDecimal , если хотите получить точное представление 11.4.

Теперь немного объясните, почему это происходит:

float и double примитивные типы в Java – это числа с плавающей запятой , где число хранится как двоичное представление доли и экспонента.

Более конкретно, значение с плавающей запятой с двойной точностью, такое как double тип, представляет собой 64-битное значение, где:

  • 1 бит обозначает знак (положительный или отрицательный).
  • 11 бит для экспоненты.
  • 52 бит для значимых цифр (дробная часть как двоичная).

Эти части объединены для получения double представления значения.

(Источник: Википедия: двойная точность )

Подробное описание того, как обрабатываются значения с плавающей запятой в Java, см. В разделе 4.2.3: Типы, форматы и значения с плавающей запятой Спецификации языка Java.

byte , char , int , long types – это номера фиксированной точки , которые являются точными представлениями чисел. В отличие от чисел с фиксированной точкой числа с плавающей запятой будут несколько раз (безопасно предполагать «большую часть времени») не смогут вернуть точное представление числа. Именно по этой причине вы 11.399999999999 в результате 5.6 + 5.8 .

Если требуется точное значение, например 1.5 или 150.1005, вы захотите использовать один из типов фиксированной точки, который будет способен точно представлять число.

Как уже упоминалось несколько раз, Java имеет class BigDecimal который будет обрабатывать очень большие числа и очень маленькие числа.

Из справочника Java API для classа BigDecimal :

Неизменяемые знаковые числа с произвольной точностью. BigDecimal состоит из произвольного значения целочисленного целочисленного значения точности и 32-разрядной целочисленной шкалы. Если ноль или положительный, масштаб – это число цифр справа от десятичной точки. Если отрицательное значение, немасштабированное значение числа умножается на десять на силу отрицания шкалы. Таким образом, значение числа, представленного BigDecimal, (немасштабированоValue × 10 ^ -scale).

Было много вопросов о переполнении стека, относящихся к числу чисел с плавающей запятой и его точности. Вот список связанных вопросов, которые могут представлять интерес:

  • Почему я вижу двойную переменную, инициализированную некоторым значением, например 21.4, как 21.399999618530273?
  • Как печатать действительно большие числа в C ++
  • Как хранится плавающая запятая? Когда это имеет значение?
  • Использовать сумму пополнения или десятичную сумму для поступления в доллары США?

Если вы действительно хотите перейти к подробным подробным сведениям о числах с плавающей запятой, посмотрите, что каждый компьютерный ученый должен знать о арифметике с плавающей точкой .

Когда вы вводите двойной номер, например, 33.33333333333333 , значение, которое вы получаете, на самом деле является ближайшим представимым значением двойной точности, которое точно:

 33.3333333333333285963817615993320941925048828125 

Разделение на 100 дает:

 0.333333333333333285963817615993320941925048828125 

который также не представляется в виде числа с двойной точностью, поэтому снова округляется до ближайшего представимого значения, что точно:

 0.3333333333333332593184650249895639717578887939453125 

Когда вы распечатываете это значение, оно снова округляется до 17 десятичных цифр, давая:

 0.33333333333333326 

Если вы просто хотите обрабатывать значения как фракции, вы можете создать class фракций, который содержит поле числителя и знаменателя.

Напишите методы для добавления, вычитания, умножения и деления, а также метода toDouble. Таким образом, вы можете избежать поплавка во время вычислений.

EDIT: Быстрая реализация,

 public class Fraction { private int numerator; private int denominator; public Fraction(int n, int d){ numerator = n; denominator = d; } public double toDouble(){ return ((double)numerator)/((double)denominator); } public static Fraction add(Fraction a, Fraction b){ if(a.denominator != b.denominator){ double aTop = b.denominator * a.numerator; double bTop = a.denominator * b.numerator; return new Fraction(aTop + bTop, a.denominator * b.denominator); } else{ return new Fraction(a.numerator + b.numerator, a.denominator); } } public static Fraction divide(Fraction a, Fraction b){ return new Fraction(a.numerator * b.denominator, a.denominator * b.numerator); } public static Fraction multiply(Fraction a, Fraction b){ return new Fraction(a.numerator * b.numerator, a.denominator * b.denominator); } public static Fraction subtract(Fraction a, Fraction b){ if(a.denominator != b.denominator){ double aTop = b.denominator * a.numerator; double bTop = a.denominator * b.numerator; return new Fraction(aTop-bTop, a.denominator*b.denominator); } else{ return new Fraction(a.numerator - b.numerator, a.denominator); } } } 

Обратите внимание, что у вас была бы такая же проблема, если бы вы использовали десятичную арифметику с ограниченной точностью и хотели иметь дело с 1/3: 0.333333333 * 3 – 0.999999999, а не 1.00000000.

К сожалению, 5.6, 5.8 и 11.4 просто не являются круглыми числами в двоичном формате, потому что они связаны с пятыми. Таким образом, их поплавок не является точным, так как 0,3333 не точно 1/3.

Если все числа, которые вы используете, представляют собой единовременные десятичные числа, и вы хотите получить точные результаты, используйте BigDecimal. Или, как говорили другие, если ваши ценности похожи на деньги в том смысле, что они все кратные 0,01, или 0,001, или что-то еще, то умножьте все на фиксированную мощность 10 и используйте int или long (сложение и вычитание тривиальны: следите за размножением).

Однако, если вы довольны бинарными вычислениями, но вы просто хотите распечатать информацию в немного более дружественном формате, попробуйте java.util.Formatter или String.format . В строке формата укажите точность меньше полной точности двойника. До 10 значащих цифр, скажем, 11.399999999999 – 11.4, поэтому результат будет почти таким же точным и читабельным для человека в случаях, когда бинарный результат очень близок к значению, требующему лишь нескольких знаков после запятой.

Точность определения зависит от того, сколько математики вы сделали с вашими числами – в общем, чем больше вы делаете, тем больше будет накапливаться ошибка, но некоторые алгоритмы накапливают ее намного быстрее, чем другие (их называют «неустойчивыми», против «стабильного» в отношении ошибок округления). Если все, что вы делаете, это добавление нескольких значений, то я предполагаю, что сброс всего одного десятичного знака точности будет разобраться. Эксперимент.

Вы можете захотеть изучить java.math.BigDecimal class java, если вам действительно нужна математика точности. Вот хорошая статья от Oracle / Sun по делу BigDecimal . В то время как вы никогда не можете представить 1/3, как кто-то упоминал, вы можете иметь право решать, насколько точно вы хотите получить результат. setScale () – ваш друг .. 🙂

Хорошо, потому что у меня слишком много времени на моих armх, вот пример кода, который относится к вашему вопросу:

 import java.math.BigDecimal; /** * Created by a wonderful programmer known as: * Vincent Stoessel * [email protected] * on Mar 17, 2010 at 11:05:16 PM */ public class BigUp { public static void main(String[] args) { BigDecimal first, second, result ; first = new BigDecimal("33.33333333333333") ; second = new BigDecimal("100") ; result = first.divide(second); System.out.println("result is " + result); //will print : result is 0.3333333333333333 } } 

и, чтобы подключить мой новый любимый язык, Groovy, вот примерный пример того же:

 import java.math.BigDecimal def first = new BigDecimal("33.33333333333333") def second = new BigDecimal("100") println "result is " + first/second // will print: result is 0.33333333333333 

Как отмечали другие, не все десятичные значения могут быть представлены как двоичные, так как десятичное число основано на степенях 10, а двоичный – на основе двух.

Если точность имеет значение, используйте BigDecimal, но если вы просто хотите дружественный вывод:

 System.out.printf("%.2f\n", total); 

Дам тебе:

 11.40 

Довольно уверен, что вы могли бы сделать это в трехстрочном примере. 🙂

Если вы хотите точную точность, используйте BigDecimal. В противном случае вы можете использовать ints, умноженное на 10 ^ любую нужную вам точность.

Вы сталкиваетесь с ограничением точности типа double.

Java.Math имеет некоторые арифметические средства произвольной точности.

Вы не можете, потому что 7.3 не имеет конечного представления в двоичном формате. Самое близкое, что вы можете получить, это 2054767329987789/2 ** 48 = 7.3 + 1/1407374883553280.

Взгляните на http://docs.python.org/tutorial/floatingpoint.html для дальнейшего объяснения. (Это на сайте Python, но Java и C ++ имеют одну и ту же «проблему».)

Решение зависит от того, что именно ваша проблема:

  • Если вам просто не нравится видеть все эти цифры шума, тогда исправьте форматирование строк. Не отображать более 15 значащих цифр (или 7 для поплавка).
  • Если это то, что неточность ваших чисел нарушает такие вещи, как «если», то вы должны написать if (abs (x – 7.3)
  • Если вы работаете с деньгами, то, что вы, вероятно, действительно хотите, это десятичная фиксированная точка. Храните целое число центов или любую наименьшую единицу вашей валюты.
  • (ОЧЕНЬ НЕВОЗМОЖНО) Если вам нужно более 53 значащих бит (15-16 значащих цифр) точности, то используйте высокоточный тип с плавающей запятой, например BigDecimal.
 private void getRound() { // this is very simple and interesting double a = 5, b = 3, c; c = a / b; System.out.println(" round val is " + c); // round val is : 1.6666666666666667 // if you want to only two precision point with double we // can use formate option in String // which takes 2 parameters one is formte specifier which // shows dicimal places another double value String s = String.format("%.2f", c); double val = Double.parseDouble(s); System.out.println(" val is :" + val); // now out put will be : val is :1.67 } 

Использовать java.math.BigDecimal

Удваивает двоичные фракции внутри, поэтому они иногда не могут представлять десятичные дроби до точного десятичного числа.

Умножьте все на 100 и сохраните его задолго, как цента.

Компьютеры хранят номера в двоичном формате и фактически не могут представлять цифры, такие как 33.333333333 или 100.0. Это одна из сложнейших вещей об использовании удвоений. Вам нужно будет просто ответить на вопрос, прежде чем показывать его пользователю. К счастью, в большинстве приложений вам не нужно столько десятичных знаков.

Числа с плавающей запятой отличаются от реальных чисел тем, что для любого заданного числа с плавающей запятой существует следующее более высокое число с плавающей запятой. То же, что и целые числа. Нет целого числа от 1 до 2.

Невозможно представить 1/3 как поплавок. Под ним есть поплавок, над ним есть поплавок, и между ними есть определенное расстояние. И 1/3 находится в этом пространстве.

Apfloat для Java претендует на работу с произвольными точками с плавающей запятой, но я никогда не использовал его. Наверное, стоит посмотреть. http://www.apfloat.org/apfloat_java/

Аналогичный вопрос задавался здесь перед библиотекой высокой точности с плавающей запятой Java

Удваивается аппроксимация десятичных чисел в вашем источнике Java. Вы видите следствие несоответствия между двойным (который является двоично-кодированным значением) и вашим источником (который имеет десятичную кодировку).

Java производит самое близкое двоичное приближение. Вы можете использовать java.text.DecimalFormat для отображения более десятичного десятичного значения.

Используйте BigDecimal. Он даже позволяет вам определять правила округления (например, ROUND_HALF_EVEN, что минимизирует статистическую ошибку, округляя до четного соседа, если оба имеют одинаковое расстояние, то есть как 1.5, так и 2.5 раунд до 2).

Проверьте BigDecimal, он обрабатывает проблемы, связанные с арифметикой с плавающей запятой.

Новый вызов будет выглядеть так:

 term[number].coefficient.add(co); 

Используйте setScale (), чтобы установить число десятичной точности.

Почему бы не использовать метод round () из classа Math?

 // The number of 0s determines how many digits you want after the floating point // (here one digit) total = (double)Math.round(total * 10) / 10; System.out.println(total); // prints 11.4 

Если у вас нет другого выбора, кроме использования двойных значений, можете использовать приведенный ниже код.

 public static double sumDouble(double value1, double value2) { double sum = 0.0; String value1Str = Double.toString(value1); int decimalIndex = value1Str.indexOf("."); int value1Precision = 0; if (decimalIndex != -1) { value1Precision = (value1Str.length() - 1) - decimalIndex; } String value2Str = Double.toString(value2); decimalIndex = value2Str.indexOf("."); int value2Precision = 0; if (decimalIndex != -1) { value2Precision = (value2Str.length() - 1) - decimalIndex; } int maxPrecision = value1Precision > value2Precision ? value1Precision : value2Precision; sum = value1 + value2; String s = String.format("%." + maxPrecision + "f", sum); sum = Double.parseDouble(s); return sum; } 

Не тратьте свое внимание на BigDecimal. В 99,99999% случаев вам это не нужно. java double type имеет приблизительный курс, но почти во всех случаях он достаточно точен. Имейте в виду, что у вас ошибка на 14-й значащей цифре. Это действительно незначительно!

Чтобы получить хороший выход, используйте:

 System.out.printf("%.2f\n", total); 
  • Как сравнить два числа с плавающей запятой в Bash?
  • Меню поддержки поддержки Android Design Library (FAB)
  • Должен ли я использовать double или float?
  • C: Приведение минимального 32-битного целого числа (-2147483648) к float дает положительное число (2147483648.0)
  • Что случилось с этим делением?
  • Как преобразовать строку в float?
  • Есть ли функция для округления поплавка на C или мне нужно написать собственное?
  • Деление с плавающей запятой против умножения с плавающей запятой
  • Быстрый метод для округления двойного до 32-битного int
  • Почему 24.0000 не равно 24.0000 в MATLAB?
  • Как красиво форматировать плавающие числа в String без ненужного десятичного числа 0?
  • Давайте будем гением компьютера.