Почему бы не использовать Double или Float для представления валюты? - PullRequest
854 голосов
/ 16 сентября 2010

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

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

Ответы [ 15 ]

2 голосов
/ 12 декабря 2017

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

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

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

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

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

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

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

1 голос
/ 25 апреля 2019

Чтобы добавить к предыдущим ответам, есть также возможность реализации Joda-Money на Java, помимо BigDecimal, при решении проблемы, рассматриваемой в вопросе. Java-модуль называется org.joda.money.

Требуется Java SE 8 или более поздняя версия и не имеет зависимостей.

Чтобы быть более точным, есть зависимость во время компиляции, но это не так требуется.

<dependency>
  <groupId>org.joda</groupId>
  <artifactId>joda-money</artifactId>
  <version>1.0.1</version>
</dependency>

Примеры использования Joda Money:

  // create a monetary value
  Money money = Money.parse("USD 23.87");

  // add another amount with safe double conversion
  CurrencyUnit usd = CurrencyUnit.of("USD");
  money = money.plus(Money.of(usd, 12.43d));

  // subtracts an amount in dollars
  money = money.minusMajor(2);

  // multiplies by 3.5 with rounding
  money = money.multipliedBy(3.5d, RoundingMode.DOWN);

  // compare two amounts
  boolean bigAmount = money.isGreaterThan(dailyWage);

  // convert to GBP using a supplied rate
  BigDecimal conversionRate = ...;  // obtained from code outside Joda-Money
  Money moneyGBP = money.convertedTo(CurrencyUnit.GBP, conversionRate, RoundingMode.HALF_UP);

  // use a BigMoney for more complex calculations where scale matters
  BigMoney moneyCalc = money.toBigMoney();

Документация: http://joda -money.sourceforge.net / apidocs / орг / Joda / деньги / Money.html

Примеры реализации: https://www.programcreek.com/java-api-examples/?api=org.joda.money.Money

1 голос
/ 18 июля 2015

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

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

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

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

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

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

Библиотека многократной точности GNU (GMP) и библиотека GNU MFPR - это два бесплатных ресурса с открытым исходным кодом для C и C ++.

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

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

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

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

Для скорости вы можете заглянуть в бесплатные и проприетарные библиотеки на C, C ++ и Fortran.

1 голос
/ 18 сентября 2010

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

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

0 голосов
/ 24 ноября 2015

Некоторые примеры ... это работает (на самом деле не работает должным образом), практически на любом языке программирования ... Я пробовал с 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!!!

...