통화를 나타 내기 위해 Double 또는 Float를 사용하지 않는 이유는 무엇입니까?
나는 항상 돈 이나 유형 을 대표 하지 말라고 들었는데 이번에는 당신에게 질문을 던졌습니다 : 왜?double
float
나는 아주 좋은 이유가 있다고 확신합니다. 나는 단순히 그것이 무엇인지 모릅니다.
수레와 복식은 우리가 돈으로 사용하는 기본 10 배수를 정확하게 나타낼 수 없기 때문입니다. 이 문제는 Java뿐만 아니라 기본 2 부동 소수점 유형을 사용하는 모든 프로그래밍 언어에 대한 문제입니다.
10 진법에서는 10.25를 1025 * 10-2 (정수 곱하기 10의 거듭 제곱) 로 쓸 수 있습니다 . IEEE-754 부동 소수점 숫자 는 다르지만, 이에 대해 생각하는 매우 간단한 방법은 대신 2의 거듭 제곱으로 곱하는 것입니다. 예를 들어 164 * 2 -4 (정수 곱하기 2의 거듭 제곱)를 볼 수 있으며 이는 10.25와 같습니다. 그것은 숫자가 메모리에서 표현되는 방식이 아니지만 수학 의미는 동일합니다.
10 진법에서도이 표기법은 대부분의 단순한 분수를 정확하게 표현할 수 없습니다. 예를 들어, 1/3을 나타낼 수 없습니다. 십진수 표현이 반복되고 (0.3333 ...), 따라서 1/3을 얻기 위해 10의 거듭 제곱을 곱할 수있는 유한 정수가 없습니다. 3의 긴 시퀀스와 333333333 * 10 -10 과 같은 작은 지수를 정할 수는 있지만 정확하지 않습니다. 3을 곱하면 1이되지 않습니다.
그러나 돈을 계산할 때, 적어도 미국 달러의 10 배 이내의 가치가있는 국가의 경우 일반적으로 10-2의 배수를 저장할 수 있으면 되므로 실제로는 중요하지 않습니다. 1/3은 표현할 수 없습니다.
실수 와 복식의 문제는 대부분 의 돈과 같은 숫자가 정수 곱하기 2의 거듭 제곱으로 정확하게 표현되지 않는다는 것입니다. 사실, 0과 1 사이의 유일한 배수는 0.01입니다. 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
유형이 있습니다.
Bloch, J., Effective Java, 2nd ed, Item 48 :
float
과double
는 0.1 (또는 임의의 다른 열 음극 전원)이 같은 표현하는 것은 불가능하기 때문에, 특히 화폐 종류 계산에 부적당된다float
또는double
정확하게한다.예를 들어 $ 1.03이고 42 센트를 지출한다고 가정합니다. 남은 돈은 얼마입니까?
System.out.println(1.03 - .42);
인쇄합니다
0.6100000000000001
.이 문제를 해결하는 올바른 방법은 사용하는 것입니다
BigDecimal
,int
또는long
금전적 계산.
BigDecimal
몇 가지 경고가 있지만 (현재 허용되는 답변을 참조하십시오).
이것은 정확성의 문제도 아니고 정확성의 문제도 아닙니다. 이는 2 진법 대신 10 진법을 계산에 사용하는 인간의 기대를 충족시키는 문제입니다. 예를 들어, 재무 계산에 double을 사용하면 수학적 의미에서 "잘못된"답이 생성되지는 않지만 다음과 같은 답이 생성 될 수 있습니다. 재정적 인 의미에서 기대되는 것이 아닙니다.
출력 전 마지막 순간에 결과를 반올림하더라도 가끔 예상과 일치하지 않는 복식을 사용하여 결과를 얻을 수 있습니다.
계산기를 사용하거나 손으로 결과를 계산하면 정확히 1.40 * 165 = 231입니다. 그러나 내부적으로 내 컴파일러 / 운영 체제 환경에서 복식을 사용하면 230.99999에 가까운 이진수로 저장됩니다 ... 따라서 숫자를 자르면 231 대신 230이됩니다. 자르기 대신 반올림하면 231이라는 원하는 결과를 얻었습니다. 사실이지만 반올림에는 항상 잘림이 포함됩니다. 어떤 반올림 기술을 사용하든, 반올림 할 것으로 예상 할 때 반올림되는 이와 같은 경계 조건이 여전히 있습니다. 그들은 종종 우연한 테스트 또는 관찰을 통해 발견되지 않을 정도로 희귀합니다. 예상대로 작동하지 않는 결과를 보여주는 예제를 검색하려면 일부 코드를 작성해야 할 수 있습니다.
가장 가까운 페니로 반올림하고 싶다고 가정합니다. 따라서 최종 결과를 가져 와서 100을 곱하고 0.5를 더한 다음 잘라낸 다음 결과를 100으로 나누어 페니로 되돌립니다. 저장 한 내부 숫자가 3.465가 아니라 3.46499999 .... 인 경우 가장 가까운 페니로 반올림하면 3.47 대신 3.46이됩니다. 하지만 10 진법 계산에 따르면 답은 정확히 3.465 여야하며, 3.46이 아닌 3.47로 반올림해야합니다. 이러한 종류의 일은 재무 계산에 복식을 사용할 때 실제 생활에서 가끔 발생합니다. 드물기 때문에 종종 문제로 눈에 띄지 않지만 발생합니다.
복식 대신 내부 계산에 10 진수를 사용하는 경우 코드에 다른 버그가 없다고 가정하면 대답은 항상 사람이 예상하는 것과 정확히 일치합니다.
이러한 응답 중 일부에 문제가 있습니다. 나는 복식과 수레가 재무 계산에서 자리를 차지한다고 생각합니다. 확실히, 분수가 아닌 금액을 더하고 뺄 때 정수 클래스 또는 BigDecimal 클래스를 사용할 때 정밀도가 손실되지 않습니다. 그러나 더 복잡한 작업을 수행 할 때 숫자를 저장하는 방법에 관계없이 소수 또는 여러 소수 자리를 벗어나는 결과가 나오는 경우가 많습니다. 문제는 결과를 제시하는 방법입니다.
결과가 반올림과 반올림 사이의 경계에 있고 마지막 페니가 정말로 중요한 경우, 더 많은 소수점 자리를 표시하여 시청자에게 대답이 거의 중간에 있음을 알려야 할 것입니다.
double의 문제, float의 문제는 큰 수와 작은 수를 결합하는 데 사용되는 경우입니다. 자바에서는
System.out.println(1000000.0f + 1.2f - 1000000.0f);
결과
1.1875
수레와 복식은 근사치입니다. BigDecimal을 만들고 float를 생성자에 전달하면 float가 실제로 무엇인지 볼 수 있습니다.
groovy:000> new BigDecimal(1.0F)
===> 1
groovy:000> new BigDecimal(1.01F)
===> 1.0099999904632568359375
이것은 아마도 당신이 $ 1.01을 나타내는 방법이 아닐 것입니다.
문제는 IEEE 사양이 모든 분수를 정확하게 표현하는 방법이 없다는 것입니다. 일부는 반복되는 분수로 끝나므로 근사 오류가 발생합니다. 회계사는 정확히 한 푼으로 나오는 것을 좋아하고 고객이 청구서를 지불하면 짜증을 낼 것이고 결제가 처리 된 후 .01을 빚지고 수수료가 부과되거나 계정을 해지 할 수 없기 때문에 사용하는 것이 좋습니다. 10 진수 (C #) 또는 Java의 java.math.BigDecimal과 같은 정확한 유형.
반올림하면 오류를 제어 할 수없는 것이 아닙니다 . Peter Lawrey의이 기사를 참조하십시오 . 처음부터 둥글게 할 필요가없는 것이 더 쉽습니다. 돈을 처리하는 대부분의 응용 프로그램은 많은 수학을 요구하지 않으며 작업은 물건을 추가하거나 다른 버킷에 금액을 할당하는 것으로 구성됩니다. 부동 소수점을 도입하고 반올림하면 상황이 복잡해집니다.
나는 반대 투표를 당할 위험이 있지만 통화 계산에 부동 소수점 숫자가 부적합하다고 생각합니다. 센트 반올림을 올바르게 수행하고 zneak에서 설명하는 이진 10 진수 표현 불일치를 해결하기 위해 작업하기에 충분한 유효 숫자가 있는지 확인하는 한 문제가 없습니다.
Excel에서 통화로 계산하는 사람들은 항상 배정 밀도 부동 소수점을 사용했으며 (Excel에는 통화 유형이 없음) 반올림 오류에 대해 불평하는 사람을 아직 보지 못했습니다.
물론, 당신은 이성 안에 있어야합니다. 예를 들어 간단한 웹샵에서는 배정 밀도 부동 소수점에 대한 문제가 전혀 발생하지 않을 것입니다. 그러나 회계 또는 많은 (무제한) 수를 추가해야하는 다른 작업을 수행하는 경우 10 피트의 부동 소수점 숫자를 만지고 싶지 않을 것입니다. 폴.
부동 소수점 유형이 대략 10 진수 데이터 만 나타낼 수 있다는 것은 사실이지만 숫자를 표시하기 전에 필요한 정밀도로 반올림하면 올바른 결과를 얻는 것도 사실입니다. 보통.
일반적으로 double 유형은 16 자리 미만의 정밀도를 갖기 때문입니다. 더 나은 정밀도가 필요한 경우 적합한 유형이 아닙니다. 또한 근사치가 누적 될 수 있습니다.
고정 소수점 산술을 사용하더라도주기적인 십진수를 얻으면 BigInteger 및 BigDecimal이 오류를 제공한다는 사실이 아니었다면 여전히 숫자를 반올림해야합니다. 그래서 여기에도 근사치가 있습니다.
예를 들어, 역사적으로 재무 계산에 사용 된 COBOL의 최대 정밀도는 18 자리입니다. 따라서 종종 암시 적 반올림이 있습니다.
결론적으로, 내 생각에 double은 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;
}
}
부동 소수점 숫자의 결과는 정확하지 않으므로 근사치가 아닌 정확한 결과가 필요한 재무 계산에는 적합하지 않습니다. float 및 double은 엔지니어링 및 과학 계산을 위해 설계되었으며 정확한 결과를 생성하지 못하는 경우가 많으며 부동 소수점 계산의 결과는 JVM마다 다를 수 있습니다. 화폐 가치를 나타내는 데 사용되는 BigDecimal 및 double primitive의 아래 예를 살펴보면 부동 소수점 계산이 정확하지 않을 수 있으며 재무 계산에 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 또는 float로 표현하는 것은 소프트웨어가 작은 오류를 반올림하므로 처음에는 좋을 것 같지만 정확하지 않은 숫자에 대해 더 많은 덧셈, 뺄셈, 곱셈 및 나눗셈을 수행하면 점점 더 정밀도를 잃게됩니다. 이로 인해 기본 10 거듭 제곱의 배수에 대한 완벽한 정확도가 요구되는 돈을 처리하기에 부적합한 수레와 두 배가됩니다. "
마지막으로 자바는 화폐와 화폐를 다루는 표준 방법을 가지고 있습니다!
JSR 354 : 화폐 및 통화 API
JSR 354는 Money 및 Currency를 사용하여 포괄적 인 계산을 표현, 전송 및 수행하기위한 API를 제공합니다. 다음 링크에서 다운로드 할 수 있습니다.
JSR 354 : Money and Currency API 다운로드
사양은 다음과 같이 구성됩니다.
- 금액 및 통화 처리를위한 API
- 상호 교환 가능한 구현을 지원하는 API
- 구현 클래스의 인스턴스를 만들기위한 팩토리
- 금액 계산, 변환 및 서식 지정 기능
- Money and Currencies 작업을위한 Java API는 Java 9에 포함될 예정입니다.
- 모든 사양 클래스와 인터페이스는 javax.money. * 패키지에 있습니다.
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 9 Money and Currency API (JSR 354) 살펴보기
계산에 다양한 단계가 포함되는 경우 임의 정밀도 산술이 100 %를 다루지 않습니다.
결과의 완벽한 표현을 사용하고 (마지막 단계로 나누기 작업을 일괄 처리하는 사용자 정의 분수 데이터 유형을 사용하는) 신뢰할 수있는 유일한 방법이며 마지막 단계에서 십진 표기법으로 만 변환합니다.
Arbitrary precision won't help because there always can be numbers that has so much decimal places, or some results such as 0.6666666... No arbitrary representation will cover the last example. So you will have small errors in each step.
This errors will add-up, may eventually become not easy to ignore anymore. This is called Error Propagation.
Most answers have highlighted the reasons why one should not use doubles for money and currency calculations. And I totally agree with them.
It doesn't mean though that doubles can never be used for that purpose.
I have worked on a number of projects with very low gc requirements, and having BigDecimal objects was a big contributor to that overhead.
It's the lack of understanding about double representation and lack of experience in handling the accuracy and precision that brings about this wise suggestion.
You can make it work if you are able to handle the precision and accuracy requirements of your project, which has to be done based on what range of double values is one dealing with.
You can refer to guava's FuzzyCompare method to get more idea. The parameter tolerance is the key. We dealt with this problem for a securities trading application and we did an exhaustive research on what tolerances to use for different numerical values in different ranges.
Also, there might be situations when you're tempted to use Double wrappers as a map key with hash map being the implementation. It is very risky because Double.equals and hash code for example values "0.5" & "0.6 - 0.1" will cause a big mess.
Many of the answers posted to this question discuss IEEE and the standards surrounding floating-point arithmetic.
Coming from a non-computer science background (physics and engineering), I tend to look at problems from a different perspective. For me, the reason why I wouldn't use a double or float in a mathematical calculation is that I would lose too much information.
What are the alternatives? There are many (and many more of which I am not aware!).
BigDecimal in Java is native to the Java language. Apfloat is another arbitrary-precision library for Java.
The decimal data type in C# is Microsoft's .NET alternative for 28 significant figures.
SciPy (Scientific Python) can probably also handle financial calculations (I haven't tried, but I suspect so).
The GNU Multiple Precision Library (GMP) and the GNU MFPR Library are two free and open-source resources for C and C++.
There are also numerical precision libraries for JavaScript(!) and I think PHP which can handle financial calculations.
There are also proprietary (particularly, I think, for Fortran) and open-source solutions as well for many computer languages.
I'm not a computer scientist by training. However, I tend to lean towards either BigDecimal in Java or decimal in C#. I haven't tried the other solutions I've listed, but they are probably very good as well.
For me, I like BigDecimal because of the methods it supports. C#'s decimal is very nice, but I haven't had the chance to work with it as much as I'd like. I do scientific calculations of interest to me in my spare time, and BigDecimal seems to work very well because I can set the precision of my floating point numbers. The disadvantage to BigDecimal? It can be slow at times, especially if you're using the divide method.
You might, for speed, look into the free and proprietary libraries in C, C++, and Fortran.
To add on previous answers, there is also option of implementing Joda-Money in Java, besides BigDecimal, when dealing with the problem addressed in the question. Java modul name is org.joda.money.
It requires Java SE 8 or later and has no dependencies.
To be more precise, there is compile-time dependency but it is not required.
<dependency>
<groupId>org.joda</groupId>
<artifactId>joda-money</artifactId>
<version>1.0.1</version>
</dependency>
Examples of using 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();
Documentation: http://joda-money.sourceforge.net/apidocs/org/joda/money/Money.html
Implementation examples: https://www.programcreek.com/java-api-examples/?api=org.joda.money.Money
Some example... this works (actually don't work as expected), on almost any programming language... I've tried with Delphi, VBScript, Visual Basic, JavaScript and now with 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!!!");
}
OUTPUT:
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!!!
참고URL : https://stackoverflow.com/questions/3730019/why-not-use-double-or-float-to-represent-currency
'development' 카테고리의 다른 글
UTF-8 byte []를 문자열로 변환하는 방법은 무엇입니까? (0) | 2020.09.28 |
---|---|
API 버전 관리에 대한 모범 사례? (0) | 2020.09.28 |
Pandas DataFrame 열 헤더에서 목록 가져 오기 (0) | 2020.09.28 |
String.Format [duplicate]에서 중괄호 '{'이스케이프 (0) | 2020.09.28 |
INNER JOIN ON 대 WHERE 절 (0) | 2020.09.28 |