또 다른 금액 계산 방법
그렇지 않으면 친숙한 반올림 오차가 발생합니다.
( @ 미야카와 타쿠의 "금 계정을위한 BigDecimal 그리고 Money and Currency API"에서)
그러나
new BigDecimal(0.1)
같은 초기화되면 원래도 아이도 없고, 여러 번 반올림 처리를 통과하면 역시 오차를 낳을 가능성이 있습니다.
그 밖에 좋은 방안은 없는 것일까요…
Ratio 타입
대부분의 Lisp은 분수형을 가지고 있다고 합니다. 조사해 보겠습니다.
Clojure
user=> (/ 22 7)
22/7
CommonLisp
[1]> (/ 22 7)
22/7
gauche
gosh> (/ 22 7)
22/7
MatzLisp
irb(main):001:0> 22r / 7
=> (22/7)
아무도 나눗셈은 분수로 취급합니다. 따라서, 반올림 처리는 계산 도중에 신경 쓸 필요가 없고, 마지막 마지막으로 1회 하면 된다.
Clojure
user=> (* 1000 (- 1 (/ 7 100)))
930N
???「돈을 받는다면 Lisp이다!!」
Java에서의 Ratio 구현
Ratio 타입의 구현은 간단합니다.
public class Ratio {
public long numerator;
public long denominator;
public Ratio(long numerator, long denominator) {
this.numerator = numerator;
this.denominator = denominator;
reduce();
}
public Ratio plus(Ratio x) {
if (x.denominator == this.denominator) {
this.numerator += x.numerator;
} else {
long d = this.denominator * x.denominator;
this.numerator = this.numerator * x.denominator + x.numerator * this.denominator;
this.denominator = d;
}
reduce();
return this;
}
public Ratio minus(Ratio x) {
if (x.denominator == this.denominator) {
this.numerator -= x.numerator;
} else {
long d = this.denominator * x.denominator;
this.numerator = this.numerator * x.denominator - x.numerator * this.denominator;
this.denominator = d;
}
reduce();
return this;
}
public Ratio multiply(Ratio x) {
this.numerator *= x.numerator;
this.denominator *= x.denominator;
reduce();
return this;
}
public Ratio devide(Ratio x) {
this.numerator *= x.denominator;
this.denominator *= x.numerator;
reduce();
return this;
}
public long quotient() {
return numerator / denominator;
}
public Ratio remainder() {
return new Ratio(numerator % denominator, denominator);
}
@Override
public boolean equals(Object anothor) {
return anothor != null
&& anothor instanceof Ratio
&& ((Ratio) anothor).numerator == numerator
&& ((Ratio) anothor).denominator == denominator;
}
private void reduce() {
long gcd = calcGcd(numerator, denominator);
numerator /= gcd;
denominator /= gcd;
}
private long calcGcd(long a, long b) {
if (b == 0) return a;
return calcGcd(b, a%b);
}
}
(실제 사용할 때는 Number 형을 상속하거나 Comparable을 구현하는 것이 좋다고 생각합니다)
아래와 같은 단점에 해당하는 경우는
numerator
와 denominator
의 형태를 BigInteger
로 해 주세요.단점
Example의 코드는 매회 약분하고 있습니다만, 되풀이해 계산하는 경우, 약분의 오버헤드가 조금 걸립니다. 한층 더 복리 계산과 같이 같은 금리를 곱하는 경우, 분자·분모가 큰 숫자가 되어 메모리를 먹는 경우가 있습니다…
장점
앞에서 설명한 단점을 극복할 수 있다면 계산 과정에서의 오차를 신경 쓸 필요가 없어지므로 안심하고 사용할 수 있게 됩니다.
요약
BigDecimal을 사용해도 문제 일어날 것 같은 프로젝트는, 분수형을 베이스로 한 금액형을 만드는 것을 검토하면 좋은 것이 아닐까요.
그렇지 않으면 Lisp을 사용합시다.
Reference
이 문제에 관하여(또 다른 금액 계산 방법), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://qiita.com/kawasima/items/4be8501f2fc32004572e텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)