Сохранить точность с двойным в 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?
20 ответов:
как уже упоминалось, вы, вероятно, хотите использовать
BigDecimalкласс, если вы хотите иметь точное представление 11.4.теперь немного объясним, почему это происходит:
The
floatиdoubleпримитивные типы в Java являются с плавающей точкой чисел, где число хранится в виде двоичного представления дроби и степени.более конкретно, плавающая двойная точность значение точки, например
doubleтип-это 64-разрядное значение, где:
- 1 бит обозначает знак (положительный или отрицательный).
- 11 бит для экспоненты.
- 52 бита для значащих цифр (дробная часть как двоичная).
эти части объединены для получения
doubleпредставление значения.(источник: Википедия: двойная точность)
для детального описания как значения с плавающей запятой обрабатываются в Java, см. раздел 4.2.3: типы, форматы и значения с плавающей запятой спецификации языка Java.
The
byte,char,int,longтипы фиксированной точки числа, которые являются точными репрессиями чисел. В отличие от чисел с фиксированной точкой, числа с плавающей точкой будут несколько раз (безопасно предположить "большую часть времени") не смогут вернуть точное представление числа. Это почему вы в конечном итоге с помощью11.399999999999в результате5.6 + 5.8.когда требуется точное значение, например 1.5 или 150.1005, вы захотите использовать один из типов с фиксированной точкой, который сможет точно представлять число.
как уже упоминалось несколько раз, в Java
BigDecimalкласс, который будет обрабатывать очень большие числа и очень маленькие числа.из ссылки Java API для
BigDecimalкласс:неизменяемый, десятичное число со знаком произвольной точности числа. BigDecimal состоит из произвольная точность целое число без масштабирования значение и 32-разрядная целочисленная шкала. Если ноль или положительное, шкала количество цифр справа от десятичная точка. Если отрицательный, то немасштабируемое значение числа умножить на десять в силу отрицание масштаба. Значение число, представленное Поэтому BigDecimal (unscaledValue × 10^-шкалы).
было много вопросов о переполнении стека, связанных с вопросом о числах с плавающей запятой и его точности. Вот список связанных вопросов, которые могут представлять интерес:
- почему я вижу двойную переменную, инициализированную до некоторого значения, например 21.4 как 21.399999618530273?
- как печатать действительно большие числа в C++
- как хранится с плавающей запятой? Когда разве это имеет значение?
- используйте Float или Decimal для учета суммы доллара приложения?
если вы действительно хотите, чтобы добраться до мельчайших деталей с плавающей запятой чисел, взгляните на Что Каждый Компьютерный Ученый Должен Знать Об Арифметике С Плавающей Запятой.
при вводе двойного числа, например,
33.33333333333333, значение, которое вы получаете, на самом деле является самым близким представимым значением двойной точности, которое точно:33.3333333333333285963817615993320941925048828125деление на 100 дает:
0.333333333333333285963817615993320941925048828125который также не представляется как число двойной точности, поэтому он снова округляется до ближайшего представимого значения, которое точно:
0.3333333333333332593184650249895639717578887939453125когда вы печатаете это значение, оно округляется еще раз до 17 десятичных цифры, дающие:
0.33333333333333326
Если вы просто хотите обрабатывать значения в виде дробей, вы можете создать класс дробей, который содержит поле числителя и знаменателя.
напишите методы для сложения, вычитания, умножения и деления, а также метод 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 в java.математика.Класс BigDecimal, если вам действительно нужна точная математика. Вот хорошая статья от Oracle / Sun on случай для BigDecimal. Пока вы не можете представлять 1/3, как кто-то упомянул, вы можете есть возможность решить, насколько точно вы хотите, чтобы результат был. setScale () - ваш друг.. :)
хорошо, потому что у меня слишком много времени на руках в данный момент вот пример кода, который относится к ваш вопрос:
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
если точность имеет значение, используйте BigDecimal, но если вы просто хотите дружественный вывод:
System.out.printf("%.2f\n", total);даст вам:
11.40
уверен, что вы могли бы сделать это в трехстрочном примере. :)
Если вы хотите точную точность, используйте BigDecimal. В противном случае вы можете использовать ints, умноженные на 10 ^ любую точность, которую вы хотите.
вы столкнулись с ограничением точности типа double.
Java.Математика имеет некоторые арифметические средства произвольной точности.
вы не можете, потому что 7.3 не имеет конечного представления в двоичном формате. Ближайший вы можете получить 2054767329987789/2**48 = 7.3+1/1407374883553280.
взгляните на http://docs.python.org/tutorial/floatingpoint.html для дальнейшего объяснения. (Это на веб-сайте Python, но Java и C++ имеют одну и ту же "проблему".)
решение зависит от того, что именно ваша проблема:
- если это то, что вам просто не нравится видеть все эти цифры шума, а затем исправить форматирование строки. Не показывайте более 15 значащих цифр (или 7 для float).
- если неточность ваших чисел нарушает такие вещи, как утверждения" если", то вы должны написать 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.математика.BigDecimal
двойники являются двоичными дробями внутри, поэтому они иногда не могут представлять десятичные дроби до точного десятичного числа.
компьютеры хранят числа в двоичном формате и не могут на самом деле представлять числа, такие как 33.333333333 или 100.0 точно. Это одна из самых сложных вещей об использовании двойников. Вам нужно будет просто округлить ответ, прежде чем показывать его пользователю. К счастью, в большинстве приложений вам не нужно так много десятичных знаков в любом случае.
числа с плавающей запятой отличаются от действительных чисел тем, что для любого заданного числа с плавающей запятой существует следующее более высокое число с плавающей запятой. То же, что и целые числа. Между 1 и 2 нет целого числа.
нет никакого способа представить 1/3 в виде поплавка. Под ним есть поплавок, и над ним есть поплавок, и между ними есть определенное расстояние. А 1/3 находится в этом пространстве.
Apfloat для Java утверждает, что работает с произвольными точными числами с плавающей запятой, но я никогда им не пользовался. Наверное, стоит посмотреть. http://www.apfloat.org/apfloat_java/
аналогичный вопрос был задан здесь раньше библиотека высокой точности Java с плавающей запятой
парный аппроксимаций десятичных чисел в вашем источнике Java. Вы видите следствие несоответствия между двойным (который является двоичным значением) и вашим источником (который является десятичным).
Java производит самое близкое двоичное приближение. Вы можете использовать Java.текст.DecimalFormat для отображения более красивого десятичного значения.
используйте BigDecimal. Он даже позволяет указать правила округления (например, ROUND_HALF_EVEN, которые минимизируют статистическую ошибку путем округления до четного соседа, если оба находятся на одинаковом расстоянии; т. е. как 1,5, так и 2,5 округляются до 2).
Проверьте BigDecimal, он обрабатывает проблемы, связанные с арифметикой с плавающей запятой, как это.
новый вызов будет выглядеть так:
term[number].coefficient.add(co);используйте setScale () для установки числа точности десятичных знаков, которые будут использоваться.
почему бы не использовать метод round() из класса 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 двойной тип, конечно, приблизительный, но почти во всех случаях он достаточно точен. Имейте в виду, что у вас есть ошибка в 14-й значащей цифре. Это действительно ничтожно мало!
чтобы получить хороший выход:
System.out.printf("%.2f\n", total);
Comments