Вопрос

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?

Это было полезно?

Решение

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Другие советы

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

33.3333333333333285963817615993320941925048828125

Разделив это на 100, получим:

0.333333333333333285963817615993320941925048828125

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

0.3333333333333332593184650249895639717578887939453125

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

0.33333333333333326

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

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

РЕДАКТИРОВАТЬ: быстрое внедрение,

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

Точность указания немного зависит от того, сколько математики вы выполнили со своими числами - в общем, чем больше вы делаете, тем больше ошибок накапливается, но некоторые алгоритмы накапливают ее намного быстрее, чем другие (их называют < !> quot; нестабильный " в отличие от " stable " в отношении ошибок округления). Если все, что вы делаете - это добавляете несколько значений, то я предполагаю, что отбрасывание только одного десятичного разряда точности все уладит. Эксперимент.

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

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

import java.math.BigDecimal;
/**
 * Created by a wonderful programmer known as:
 * Vincent Stoessel
 * xaymaca@gmail.com
 * 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. В противном случае вы можете использовать целые числа, умноженные на 10, с любой точностью, которую вы хотите.

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

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

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

даст вам:

11.40

Вы столкнулись с ограничением точности типа 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», тогда вам следует написать if (abs (x - 7,3) < ДОПУСК) вместо if (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. Вы видите последствия несоответствия между значением double (которое является двоичным кодом) и вашим источником (который имеет десятичное значение).

Java производит самое близкое двоичное приближение. Вы можете использовать java.text.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,9999% случаев вам это не нужно. Тип java double , конечно, приблизительный, но почти во всех случаях он достаточно точен. Имейте в виду, что у вас есть ошибка в 14-й значащей цифре. Это действительно незначительно!

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

System.out.printf("%.2f\n", total);
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top