Почему бы не использовать Double или Float для представления валюты?

мне всегда говорили никогда представлять деньги с double или float типы, и на этот раз я задаю вопрос вам: почему?

Я уверен, что есть очень хорошая причина, я просто не знаю, что это такое.

14 ответов


потому что поплавки и двойники не могут точно представлять собой базовые 10 кратные, которые мы используем для денег. Эта проблема не только для Java, это для любого языка программирования, который использует базовые 2 типа с плавающей запятой.

в базе 10 вы можете написать 10.25 как 1025 * 10-2 (целое число, умноженное на 10). IEEE-754 числа с плавающей запятой разные,но очень простой способ думать о них-это умножить на две степени. Например, вы может быть, глядя на 164 * 2-4 (целое число, умноженное на два), которое также равно 10.25. Это не то, как числа представлены в памяти, но математические выводы одинаковы.

даже в базе 10 эта нотация не может точно представлять большинство простых дробей. Например, вы не можете представить 1/3: десятичное представление повторяется (0.3333...), поэтому нет конечное число, можно умножить на 10, чтобы получить 1/3. Вы могли бы остановиться на длинная последовательность из 3-х и небольшой показатель, как 333333333 * 10-10, но это не точно: если вы умножите это на 3, вы не получите 1.

однако, с целью подсчета денег, по крайней мере для стран, чьи деньги оцениваются в пределах порядка величины доллара США, обычно все, что вам нужно, это иметь возможность хранить кратные 10-2, поэтому не имеет значения, что 1/3 не может быть представлена.

проблема с поплавками и парным что подавляющее большинство денежных чисел не имеют точного представления как целое число раз в степени 2. Фактически, единственными кратными 0.01 между 0 и 1 (которые важны при работе с деньгами, потому что они целочисленные центы), которые могут быть представлены точно как двоичное число с плавающей запятой IEEE-754, являются 0, 0.25, 0.5, 0.75 и 1. Все остальные отстают на небольшую сумму. По аналогии с примером 0.333333, если взять значение с плавающей запятой для 0.1 и умножьте его на 10, и вы не получите 1.

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

решение, которое работает практически на любом языке, заключается в использовании целых чисел и подсчете центов. Например, 1025 будет стоить 10,25 доллара. Несколько языков также имеют встроенные типы для работы с деньгами. Среди прочего, Java имеет BigDecimal класс, А C# имеет decimal тип.


от Блоха, J., эффективная Java, 2-е изд, пункт 48:

на float и double типы особенно плохо подходит для денежных расчеты, потому что это невозможно представлять 0.1 (или любой другой отрицательная сила десяти) как float или double точно.

например, предположим, что у вас есть $1.03 и вы тратите 42c. Сколько денег вы уехали?

System.out.println(1.03 - .42);

выводит 0.6100000000000001.

правильный способ решить эту проблему использовать BigDecimal, int или long для денежных расчетов.


Это не вопрос точности, и это не вопрос точности. Это вопрос удовлетворения ожиданий людей, которые используют базу 10 для расчетов вместо базы 2. Например, использование двойников для финансовых расчетов не дает ответов, которые являются "неправильными" в математическом смысле, но может дать ответы, которые не являются ожидаемыми в финансовом смысле.

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

используя калькулятор или вычисляя результаты вручную, 1.40 * 165 = 231 точно. Однако, внутренне используя Double, в моей среде компилятора / операционной системы он хранится как двоичное число, близкое к 230.99999... поэтому, если вы усечете число, вы получите 230 вместо 231. Вы можете предположить, что округление вместо усечения дало бы желаемый результат 231. Это верно, но округление всегда включает усечение. Какой бы метод округления вы ни использовали, все равно существуют граничные условия, подобные этому, которые будут округляться, когда вы ожидаете, что они округлятся. Они достаточно редки, чтобы их часто не находили путем случайного тестирования или наблюдения. Возможно, вам придется написать код для поиска примеров, иллюстрирующих результаты, которые ведут себя не так, как ожидалось.

Предположим, вы хотите округлить что-то до ближайшего Пенни. Итак, вы берете свой конечный результат, умножаете на 100, добавляете 0.5, усечь, а затем разделить результат на 100, чтобы вернуться к Пенни. Если внутренний номер сохранен был 3.46499999.... вместо 3,465 вы получите 3,46 вместо 3,47, когда округлите число до ближайшего Пенни. Но ваши расчеты базы 10, возможно, показали, что ответ должен быть 3.465 точно, что явно должно округляться до 3.47, а не до 3.46. Такие вещи иногда случаются в реальной жизни, когда вы используете двойников для финансовых расчетов. Это редкость, так что это часто остается незамеченным как проблема, но это случается.

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


меня беспокоят некоторые из этих ответов. Я думаю, что двойники и поплавки имеют место в финансовых расчетах. Конечно, при сложении и вычитании не дробных денежных сумм не будет потери точности при использовании целочисленных классов или классов BigDecimal. Но при выполнении более сложных операций, вы часто заканчиваете с результатами, которые выходят несколько или много десятичных знаков, независимо от того, как вы храните числа. Вопрос в том, как вы представляете результат.

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

проблема с двойниками и тем более с поплавками, когда они используются для объединения больших чисел и малых чисел. На java,

System.out.println(1000000.0f + 1.2f - 1000000.0f);

результаты

1.1875

float и Double являются приблизительными. Если вы создадите BigDecimal и передадите float в конструктор, вы увидите, что float фактически равен:

groovy:000> new BigDecimal(1.0F)
===> 1
groovy:000> new BigDecimal(1.01F)
===> 1.0099999904632568359375

вероятно, это не так, как вы хотите представить $1.01.

проблема в том, что спецификация IEEE не имеет способа точно представить все фракции, некоторые из них заканчиваются как повторяющиеся фракции, поэтому вы заканчиваете с ошибками аппроксимации. Так как бухгалтеры любят, чтобы вещи выходили ровно на копейки, а клиенты будет раздражен ,если они оплачивают свой счет, и после обработки платежа они должны.01 и они получают плату или не могут закрыть свой счет, лучше использовать точные типы, такие как decimal (в C#) или java.математика.BigDecimal в Java.

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


результат числа с плавающей запятой не является точным, что делает их непригодными для любого финансового расчета, который требует точного результата, а не приближения. поплавок и двойник конструированы для инженерства и научного вычисления и много времен не производят точный результат также результат вычисления с плавающей запятой может поменять от JVM к JVM. Посмотрите на ниже пример bigdecimal и double примитива, который используется для представления денежной стоимости, совершенно ясно, что расчет с плавающей запятой может быть, неточно, и для финансовых расчетов следует использовать BigDecimal.

    // floating point calculation
    final double amount1 = 2.0;
    final double amount2 = 1.1;
    System.out.println("difference between 2.0 and 1.1 using double is: " + (amount1 - amount2));

    // Use BigDecimal for financial calculation
    final BigDecimal amount3 = new BigDecimal("2.0");
    final BigDecimal amount4 = new BigDecimal("1.1");
    System.out.println("difference between 2.0 and 1.1 using BigDecimal is: " + (amount3.subtract(amount4)));

выход:

difference between 2.0 and 1.1 using double is: 0.8999999999999999
difference between 2.0 and 1.1 using BigDecimal is: 0.9

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

обычно потому, что тип double имеет точность менее 16 цифр. Если вам нужна лучшая точность, это не подходящий тип. Также могут накапливаться приближения.

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

например, COBOL, исторически используемый для финансовых расчетов, имеет максимальную точность 18 цифр. Поэтому часто существует неявное округление.

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

рассмотрим следующий вывод последующей программы. Он показывает, что после округления double дают тот же результат, что и BigDecimal до точности 16.

Precision 14
------------------------------------------------------
BigDecimalNoRound             : 56789.012345 / 1111111111 = Non-terminating decimal expansion; no exact representable decimal result.
DoubleNoRound                 : 56789.012345 / 1111111111 = 5.111011111561101E-5
BigDecimal                    : 56789.012345 / 1111111111 = 0.000051110111115611
Double                        : 56789.012345 / 1111111111 = 0.000051110111115611

Precision 15
------------------------------------------------------
BigDecimalNoRound             : 56789.012345 / 1111111111 = Non-terminating decimal expansion; no exact representable decimal result.
DoubleNoRound                 : 56789.012345 / 1111111111 = 5.111011111561101E-5
BigDecimal                    : 56789.012345 / 1111111111 = 0.0000511101111156110
Double                        : 56789.012345 / 1111111111 = 0.0000511101111156110

Precision 16
------------------------------------------------------
BigDecimalNoRound             : 56789.012345 / 1111111111 = Non-terminating decimal expansion; no exact representable decimal result.
DoubleNoRound                 : 56789.012345 / 1111111111 = 5.111011111561101E-5
BigDecimal                    : 56789.012345 / 1111111111 = 0.00005111011111561101
Double                        : 56789.012345 / 1111111111 = 0.00005111011111561101

Precision 17
------------------------------------------------------
BigDecimalNoRound             : 56789.012345 / 1111111111 = Non-terminating decimal expansion; no exact representable decimal result.
DoubleNoRound                 : 56789.012345 / 1111111111 = 5.111011111561101E-5
BigDecimal                    : 56789.012345 / 1111111111 = 0.000051110111115611011
Double                        : 56789.012345 / 1111111111 = 0.000051110111115611013

Precision 18
------------------------------------------------------
BigDecimalNoRound             : 56789.012345 / 1111111111 = Non-terminating decimal expansion; no exact representable decimal result.
DoubleNoRound                 : 56789.012345 / 1111111111 = 5.111011111561101E-5
BigDecimal                    : 56789.012345 / 1111111111 = 0.0000511101111156110111
Double                        : 56789.012345 / 1111111111 = 0.0000511101111156110125

Precision 19
------------------------------------------------------
BigDecimalNoRound             : 56789.012345 / 1111111111 = Non-terminating decimal expansion; no exact representable decimal result.
DoubleNoRound                 : 56789.012345 / 1111111111 = 5.111011111561101E-5
BigDecimal                    : 56789.012345 / 1111111111 = 0.00005111011111561101111
Double                        : 56789.012345 / 1111111111 = 0.00005111011111561101252

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.MathContext;

public class Exercise {
    public static void main(String[] args) throws IllegalArgumentException,
            SecurityException, IllegalAccessException,
            InvocationTargetException, NoSuchMethodException {
        String amount = "56789.012345";
        String quantity = "1111111111";
        int [] precisions = new int [] {14, 15, 16, 17, 18, 19};
        for (int i = 0; i < precisions.length; i++) {
            int precision = precisions[i];
            System.out.println(String.format("Precision %d", precision));
            System.out.println("------------------------------------------------------");
            execute("BigDecimalNoRound", amount, quantity, precision);
            execute("DoubleNoRound", amount, quantity, precision);
            execute("BigDecimal", amount, quantity, precision);
            execute("Double", amount, quantity, precision);
            System.out.println();
        }
    }

    private static void execute(String test, String amount, String quantity,
            int precision) throws IllegalArgumentException, SecurityException,
            IllegalAccessException, InvocationTargetException,
            NoSuchMethodException {
        Method impl = Exercise.class.getMethod("divideUsing" + test, String.class,
                String.class, int.class);
        String price;
        try {
            price = (String) impl.invoke(null, amount, quantity, precision);
        } catch (InvocationTargetException e) {
            price = e.getTargetException().getMessage();
        }
        System.out.println(String.format("%-30s: %s / %s = %s", test, amount,
                quantity, price));
    }

    public static String divideUsingDoubleNoRound(String amount,
            String quantity, int precision) {
        // acceptance
        double amount0 = Double.parseDouble(amount);
        double quantity0 = Double.parseDouble(quantity);

        //calculation
        double price0 = amount0 / quantity0;

        // presentation
        String price = Double.toString(price0);
        return price;
    }

    public static String divideUsingDouble(String amount, String quantity,
            int precision) {
        // acceptance
        double amount0 = Double.parseDouble(amount);
        double quantity0 = Double.parseDouble(quantity);

        //calculation
        double price0 = amount0 / quantity0;

        // presentation
        MathContext precision0 = new MathContext(precision);
        String price = new BigDecimal(price0, precision0)
                .toString();
        return price;
    }

    public static String divideUsingBigDecimal(String amount, String quantity,
            int precision) {
        // acceptance
        BigDecimal amount0 = new BigDecimal(amount);
        BigDecimal quantity0 = new BigDecimal(quantity);
        MathContext precision0 = new MathContext(precision);

        //calculation
        BigDecimal price0 = amount0.divide(quantity0, precision0);

        // presentation
        String price = price0.toString();
        return price;
    }

    public static String divideUsingBigDecimalNoRound(String amount, String quantity,
            int precision) {
        // acceptance
        BigDecimal amount0 = new BigDecimal(amount);
        BigDecimal quantity0 = new BigDecimal(quantity);

        //calculation
        BigDecimal price0 = amount0.divide(quantity0);

        // presentation
        String price = price0.toString();
        return price;
    }
}

Я рискну быть пониженным, но я думаю, что непригодность чисел с плавающей запятой для валютных расчетов переоценена. До тех пор, пока вы убедитесь, что вы правильно округляете цент и имеете достаточно значащих цифр для работы, чтобы противостоять несоответствию двоично-десятичного представления, объясненному zneak, проблем не будет.

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

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


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

наконец, Java имеет стандартный способ работайте с валютой и деньгами!

JSR 354: деньги и валюта API

JSR 354 предоставляет API для представления, транспортировки и выполнения комплексных расчетов с деньгами и валютой. Вы можете скачать его по этой ссылке:

JSR 354: деньги и валюта API скачать

спецификация состоит из следующих вещей:

  1. API для обработки электронной. г. денежных сумм и валют
  2. API для поддержки взаимозаменяемых реализаций
  3. фабрики для создания экземпляров классов реализации
  4. функциональность для расчетов, конвертации и форматирования денежных сумм
  5. Java API для работы с деньгами и валютами, который планируется включить в Java 9.
  6. все классы спецификации и интерфейсы находятся в класса javax.деньги.* пакет.

примеры примеров JSR 354: деньги и валюта API:

пример создания MonetaryAmount и печати его на консоли выглядит следующим образом::

MonetaryAmountFactory<?> amountFactory = Monetary.getDefaultAmountFactory();
MonetaryAmount monetaryAmount = amountFactory.setCurrency(Monetary.getCurrency("EUR")).setNumber(12345.67).create();
MonetaryAmountFormat format = MonetaryFormats.getAmountFormat(Locale.getDefault());
System.out.println(format.format(monetaryAmount));

при использовании API ссылочной реализации необходимый код намного проще:

MonetaryAmount monetaryAmount = Money.of(12345.67, "EUR");
MonetaryAmountFormat format = MonetaryFormats.getAmountFormat(Locale.getDefault());
System.out.println(format.format(monetaryAmount));

API также поддерживает вычисления с MonetaryAmounts:

MonetaryAmount monetaryAmount = Money.of(12345.67, "EUR");
MonetaryAmount otherMonetaryAmount = monetaryAmount.divide(2).add(Money.of(5, "EUR"));

CurrencyUnit и MonetaryAmount

// getting CurrencyUnits by locale
CurrencyUnit yen = MonetaryCurrencies.getCurrency(Locale.JAPAN);
CurrencyUnit canadianDollar = MonetaryCurrencies.getCurrency(Locale.CANADA);

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

MonetaryAmount monetaryAmount = Money.of(123.45, euro);
CurrencyUnit currency = monetaryAmount.getCurrency();
NumberValue numberValue = monetaryAmount.getNumber();

int intValue = numberValue.intValue(); // 123
double doubleValue = numberValue.doubleValue(); // 123.45
long fractionDenominator = numberValue.getAmountFractionDenominator(); // 100
long fractionNumerator = numberValue.getAmountFractionNumerator(); // 45
int precision = numberValue.getPrecision(); // 5

// NumberValue extends java.lang.Number. 
// So we assign numberValue to a variable of type Number
Number number = numberValue;

MonetaryAmounts можно округлить с помощью оператора округления:

CurrencyUnit usd = MonetaryCurrencies.getCurrency("USD");
MonetaryAmount dollars = Money.of(12.34567, usd);
MonetaryOperator roundingOperator = MonetaryRoundings.getRounding(usd);
MonetaryAmount roundedDollars = dollars.with(roundingOperator); // USD 12.35

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

List<MonetaryAmount> amounts = new ArrayList<>();
amounts.add(Money.of(2, "EUR"));
amounts.add(Money.of(42, "USD"));
amounts.add(Money.of(7, "USD"));
amounts.add(Money.of(13.37, "JPY"));
amounts.add(Money.of(18, "USD"));

Пользовательские MonetaryAmount операции

// A monetary operator that returns 10% of the input MonetaryAmount
// Implemented using Java 8 Lambdas
MonetaryOperator tenPercentOperator = (MonetaryAmount amount) -> {
  BigDecimal baseAmount = amount.getNumber().numberValue(BigDecimal.class);
  BigDecimal tenPercent = baseAmount.multiply(new BigDecimal("0.1"));
  return Money.of(tenPercent, amount.getCurrency());
};

MonetaryAmount dollars = Money.of(12.34567, "USD");

// apply tenPercentOperator to MonetaryAmount
MonetaryAmount tenPercentDollars = dollars.with(tenPercentOperator); // USD 1.234567

ресурсы:

обработка денег и валют в Java с JSR 354

просмотр API денег и валюты Java 9 (JSR 354)

Читайте Также: JSR 354-валюта и деньги


Если ваши вычисления включают различные шаги, произвольная арифметика точности не покроет вас 100%.

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

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

эти ошибки будут складываться, в конечном итоге может стать нелегко игнорировать больше. Это называется Распространения Ошибок.


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

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

Я работал над рядом проектов с очень низкими требованиями gc, и наличие BigDecimal объектов было большим вкладом в эти накладные расходы.

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

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

вы можете обратиться к методу FuzzyCompare гуавы, чтобы получить больше идей. Ключ-допуск параметра. Мы рассмотрели эту проблему для приложения для торговли ценными бумагами, и мы провели исчерпывающее исследование какие допуски использовать для различных числовых значений в разных диапазонах.

кроме того, могут возникнуть ситуации, когда вы соблазняетесь использовать двойные обертки в качестве ключа карты с хэш-картой, являющейся реализацией. Это очень рискованно, потому что двойное.equals и хэш-код, например, значения " 0.5 " & "0.6 - 0.1" вызовут большой беспорядок.


Я предпочитаю использовать Integer или Long для представления валюты. BigDecimal выбрасывает исходный код слишком много.

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


многие ответы, опубликованные на этот вопрос, обсуждают IEEE и стандарты, окружающие арифметику с плавающей запятой.

исходя из не-компьютерных наук (физика и техника), я склонен смотреть на проблемы с другой точки зрения. Для меня причина, по которой я не буду использовать double или float в математических расчетах, заключается в том, что я потеряю слишком много информации.

каковы альтернативы? Есть много (и еще много, из которых я не являюсь в курсе!).

BigDecimal в Java является родным для языка Java. Apfloat-еще одна библиотека произвольной точности для Java.

десятичный тип данных в C# является альтернативой Microsoft .NET для 28 значимых цифр.

SciPy (Scientific Python), вероятно, также может обрабатывать финансовые вычисления (я не пробовал, но подозреваю, что так).

библиотека GNU Multiple Precision Library (GMP) и библиотека GNU MFPR являются двумя свободными ресурсами с открытым исходным кодом для C и С.++

существуют также библиотеки числовой точности для JavaScript(!) и я думаю PHP, который может обрабатывать финансовые расчеты.

есть также собственные (особенно, я думаю, для Fortran) и решения с открытым исходным кодом, а также для многих компьютерных языков.

Я не компьютерщик по образованию. Однако я склоняюсь к BigDecimal в Java или decimal в C#. Я не пробовал другие решения, которые я перечислил, но они, вероятно, очень тоже хорошо.

для меня, мне нравится bigdecimal из-за методов, которые он поддерживает. Десятичная дробь C#очень хороша, но у меня не было возможности работать с ней столько, сколько хотелось бы. В свободное время я занимаюсь интересными мне научными вычислениями, и BigDecimal, кажется, работает очень хорошо, потому что я могу установить точность моих чисел с плавающей запятой. Недостаток BigDecimal? Иногда это может быть медленным, особенно если вы используете метод divide.

вы могли бы, для скорости, посмотрите в свободные и проприетарные библиотеки на C, C++ и Fortran.


какой-то пример... это работает (на самом деле не работает так, как ожидалось), практически на любом языке программирования... Я пробовал с Delphi, VBScript, Visual Basic, JavaScript и теперь с Java / Android:

    double total = 0.0;

    // do 10 adds of 10 cents
    for (int i = 0; i < 10; i++) {
        total += 0.1;  // adds 10 cents
    }

    Log.d("round problems?", "current total: " + total);

    // looks like total equals to 1.0, don't?

    // now, do reverse
    for (int i = 0; i < 10; i++) {
        total -= 0.1;  // removes 10 cents
    }

    // looks like total equals to 0.0, don't?
    Log.d("round problems?", "current total: " + total);
    if (total == 0.0) {
        Log.d("round problems?", "is total equal to ZERO? YES, of course!!");
    } else {
        Log.d("round problems?", "is total equal to ZERO? NO... thats why you should not use Double for some math!!!");
    }

выход:

round problems?: current total: 0.9999999999999999 round problems?: current total: 2.7755575615628914E-17 round problems?: is total equal to ZERO? NO... thats why you should not use Double for some math!!!