[javascript] JavaScript의 정확한 재무 계산. Gotchas는 무엇입니까?

크로스 플랫폼 코드를 만들기 위해 JavaScript로 간단한 금융 애플리케이션을 개발하고 싶습니다. 필요한 계산에는 복리와 비교적 긴 십진수가 포함됩니다. JavaScript를 사용하여 이러한 유형의 수학을 수행 할 때 피해야 할 실수가 무엇인지 알고 싶습니다. 가능하다면!



답변

십진수 값을 100으로 조정하고 모든 통화 값을 전체 센트로 표시해야합니다. 이는 부동 소수점 논리 및 산술 문제 를 방지하기위한 것입니다 . JavaScript에는 10 진수 데이터 유형이 없습니다. 유일한 숫자 데이터 유형은 부동 소수점입니다. 따라서 일반적으로 돈 255025.50달러 대신 센트로 처리하는 것이 좋습니다 .

JavaScript에서 고려하십시오.

var result = 1.0 + 2.0;     // (result === 3.0) returns true

그러나:

var result = 0.1 + 0.2;     // (result === 0.3) returns false

표현식은를 0.1 + 0.2 === 0.3반환 false하지만 다행스럽게도 부동 소수점의 정수 산술은 정확하므로 10 진수 표현 오류는 스케일링 1 로 피할 수 있습니다 .

실수 세트는 무한하지만 한정된 수 (정확히 18,437,736,874,454,810,627) 만 JavaScript 부동 소수점 형식으로 정확하게 표현 될 수 있습니다. 따라서 다른 숫자의 표현은 실제 숫자 2 의 근사치입니다 .


1 Douglas Crockford : JavaScript : 좋은 부분 : 부록 A-끔찍한 부분 (페이지 105) .
2 David Flanagan : JavaScript : The Definitive Guide, Fourth Edition : 3.1.3 부동 소수점 리터럴 (페이지 31) .


답변

모든 값을 100으로 조정하는 것이 해결책입니다. 손으로하는 것은 아마도 쓸모가 없을 것입니다. 그렇게 해주는 라이브러리를 찾을 수 있기 때문입니다. ES6 애플리케이션에 적합한 기능적 API를 제공하는 moneysafe를 권장합니다.

const { in$, $ } = require('moneysafe');
console.log(in$($(10.5) + $(.3)); // 10.8

https://github.com/ericelliott/moneysafe

Node.js와 브라우저에서 모두 작동합니다.


답변

소수 두 자릿수로 인해 “정확한”재무 계산과 같은 것은 없지만 더 일반적인 문제입니다.

JavaScript에서는 모든 값을 100으로 확장 Math.round()하고 분수가 발생할 때마다 사용할 수 있습니다.

객체를 사용하여 숫자를 저장하고 프로토 타입 valueOf()메서드에 반올림을 포함 할 수 있습니다 . 이렇게 :

sys = require('sys');

var Money = function(amount) {
        this.amount = amount;
    }
Money.prototype.valueOf = function() {
    return Math.round(this.amount*100)/100;
}

var m = new Money(50.42355446);
var n = new Money(30.342141);

sys.puts(m.amount + n.amount); //80.76569546
sys.puts(m+n); //80.76

이렇게하면 Money 개체를 사용할 때마다 소수점 이하 두 자리로 반올림하여 표시됩니다. 반올림되지 않은 값은 여전히를 통해 액세스 할 수 있습니다 m.amount.

원하는 Money.prototype.valueOf()경우 자체 반올림 알고리즘을으로 빌드 할 수 있습니다 .


답변

use decimaljs … 문제의 가혹한 부분을 해결하는 아주 좋은 라이브러리입니다 …

모든 작업에 사용하십시오.

https://github.com/MikeMcl/decimal.js/


답변

문제는 부동 소수점 계산의 부정확성으로 인해 발생합니다. 이 문제를 해결하기 위해 반올림 만 사용하는 경우 곱하고 나눌 때 더 큰 오류가 발생합니다.

해결책은 다음과 같습니다. 설명은 다음과 같습니다.

이를 이해하려면이 뒤에있는 수학에 대해 생각해야합니다. 1/3과 같은 실수는 끝이 없기 때문에 10 진수 값으로 수학에서 표현할 수 없습니다 (예 : .333333333333333 …). 소수의 일부 숫자는 이진수로 올바르게 표현할 수 없습니다. 예를 들어 0.1은 제한된 자릿수로 이진수로 올바르게 표현 될 수 없습니다.

자세한 설명은 여기를 참조 하십시오 : http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

솔루션 구현을 살펴보십시오. http://floating-point-gui.de/languages/javascript/


답변

인코딩의 이진 특성으로 인해 일부 십진수는 완벽한 정확도로 표현할 수 없습니다. 예를 들면

var money = 600.90;
var price = 200.30;
var total = price * 3;

// Outputs: false
console.log(money >= total);

// Outputs: 600.9000000000001
console.log(total);

순수한 자바 스크립트를 사용해야한다면 모든 계산에 대한 해결책을 생각해야합니다. 위 코드의 경우 소수를 정수로 변환 할 수 있습니다.

var money = 60090;
var price = 20030;
var total = price * 3;

// Outputs: true
console.log(money >= total);

// Outputs: 60090
console.log(total);

JavaScript에서 십진 수학 문제 방지

훌륭한 문서와 함께 재무 계산을위한 전용 라이브러리가 있습니다. Finance.js


답변

불행히도 지금까지 모든 답변은 모든 통화가 100 개의 하위 단위를 가지고 있지 않다는 사실을 무시합니다 (예 : 센트는 미국 달러 (USD)의 하위 단위). 이라크 디나르 (IQD)와 같은 통화에는 1000 개의 하위 단위가 있습니다. 이라크 디나르에는 1000 개의 필이 있습니다. 일본 엔 (JPY)에는 하위 단위가 없습니다. 따라서 “정수 연산을 위해 100을 곱한다”는 것이 항상 정답은 아닙니다.

또한 통화 계산을 위해 통화를 추적해야합니다. 인도 루피 (INR)에 미국 달러 (USD)를 추가 할 수 없습니다 (먼저 하나를 다른 루피로 변환하지 않고).

JavaScript의 정수 데이터 유형으로 표현할 수있는 최대량에도 제한이 있습니다.

화폐 계산에서 돈은 유한 한 정밀도 (일반적으로 소수점 이하 0 ~ 3 점)를 가지며 특정 방식으로 반올림해야합니다 (예 : “정상”반올림 대 은행가의 반올림). 수행 할 반올림 유형은 관할권 / 통화에 따라 다를 수도 있습니다.

자바 스크립트에서 돈을 처리하는 방법 은 관련 요점에 대한 좋은 토론을 가지고 있습니다.

내 검색에서 나는 금전적 계산과 관련된 많은 문제해결 하는 dinero.js 라이브러리를 발견했다 . 아직 프로덕션 시스템에서 사용하지 않았으므로 정보에 입각 한 의견을 제시 할 수 없습니다.