[javascript] JavaScript에서 부동 소수점 숫자 정밀도를 처리하는 방법은 무엇입니까?

다음과 같은 더미 테스트 스크립트가 있습니다.

function test() {
  var x = 0.1 * 0.2;
  document.write(x);
}
test();

이렇게하면 결과가 인쇄 0.020000000000000004되지만 인쇄하는 동안 0.02계산기가 사용됩니다. 내가 이해하는 한 이것은 부동 소수점 곱셈 정밀도의 오류 때문입니다.

그런 경우에 나는 올바른 결과를 얻을 수있는 좋은 해결책이 0.02있습니까? 나는 toFixed반올림 과 같은 기능이 있거나 또 다른 가능성이 있다는 것을 알고 있지만 실제로는 절단과 반올림없이 정수를 인쇄하고 싶습니다. 당신 중 하나가 멋지고 우아한 해결책을 가지고 있는지 알고 싶었습니다.

물론 그렇지 않으면 약 10 자리 정도로 반올림합니다.



답변

로부터 부동 소수점 가이드 :

이 문제를 피하려면 어떻게해야합니까?

그것은 당신이하고있는 계산의 종류에 달려 있습니다.

  • 특히 돈을 다룰 때 결과를 정확하게 합치려면 특별한 10 진수 데이터 유형을 사용하십시오.
  • 추가 소수 자릿수를 모두 표시하지 않으려면 결과를 표시 할 때 고정 소수점 이하 자릿수로 반올림하십시오.
  • 사용 가능한 10 진수 데이터 유형이없는 경우 다른 방법은 정수를 사용하는 것입니다 (예 : 돈 계산을 센트 단위로 수행). 그러나 이것은 더 많은 작업이며 몇 가지 단점이 있습니다.

첫 번째 요점은 특정한 정확한 십진법 행동 이 실제로 필요한 경우에만 적용됩니다 . 대부분의 사람들은 그것을 필요로하지 않으며 단지 1/3에서 발생하면 동일한 오류로 깜박이지 않을 것이라는 사실을 깨닫지 않고 1/10과 같은 숫자로 프로그램이 올바르게 작동하지 않는다는 것에 자극을 받았습니다.

첫 번째 요점이 실제로 적용되는 경우 전혀 우아하지는 않지만 불완전한 해결 방법을 제공하는 대신 문제를 해결하는 JavaScript에 BigDecimal for JavaScript를 사용 하십시오.


답변

나는 Pedro Ladaria의 솔루션을 좋아하고 비슷한 것을 사용합니다.

function strip(number) {
    return (parseFloat(number).toPrecision(12));
}

Pedros 솔루션과 달리 이것은 0.999를 반올림합니다 … 반복하며 가장 작은 자리에서 1을 더하기 / 빼기 정확합니다.

참고 : 32 비트 또는 64 비트 부동 소수점을 처리 할 때 최상의 결과를 얻으려면 toPrecision (7) 및 toPrecision (15)을 사용해야합니다. 이유에 대한 정보는 이 질문 을 참조하십시오 .


답변

수학적으로 기울어 진 경우 : http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

권장되는 방법은 보정 계수를 사용하는 것입니다 (적수 사이에 산술이 발생하도록 10의 적절한 거듭 제곱을 곱함). 예를 들어의 경우 0.1 * 0.2보정 계수는 10이며 계산을 수행하는 중입니다.

> var x = 0.1
> var y = 0.2
> var cf = 10
> x * y
0.020000000000000004
> (x * cf) * (y * cf) / (cf * cf)
0.02

(매우 빠른) 솔루션은 다음과 같습니다.

var _cf = (function() {
  function _shift(x) {
    var parts = x.toString().split('.');
    return (parts.length < 2) ? 1 : Math.pow(10, parts[1].length);
  }
  return function() {
    return Array.prototype.reduce.call(arguments, function (prev, next) { return prev === undefined || next === undefined ? undefined : Math.max(prev, _shift (next)); }, -Infinity);
  };
})();

Math.a = function () {
  var f = _cf.apply(null, arguments); if(f === undefined) return undefined;
  function cb(x, y, i, o) { return x + f * y; }
  return Array.prototype.reduce.call(arguments, cb, 0) / f;
};

Math.s = function (l,r) { var f = _cf(l,r); return (l * f - r * f) / f; };

Math.m = function () {
  var f = _cf.apply(null, arguments);
  function cb(x, y, i, o) { return (x*f) * (y*f) / (f * f); }
  return Array.prototype.reduce.call(arguments, cb, 1);
};

Math.d = function (l,r) { var f = _cf(l,r); return (l * f) / (r * f); };

이 경우 :

> Math.m(0.1, 0.2)
0.02

SinfulJS 와 같은 테스트 라이브러리를 사용하는 것이 좋습니다.


답변

곱셈 만 수행하고 있습니까? 그렇다면 십진 산술에 대한 깔끔한 비밀을 유리하게 사용할 수 있습니다. 그게 다야 NumberOfDecimals(X) + NumberOfDecimals(Y) = ExpectedNumberOfDecimals. 즉, 우리는 0.123 * 0.12그렇다면 소수점 0.1233 자리와 0.122 자리가 있기 때문에 소수점 5 자리가 있음을 알 수 있습니다 . 따라서 JavaScript가 우리에게 숫자를 주면 0.014760000002정밀도를 잃을 염려없이 소수점 5 자리로 안전하게 반올림 할 수 있습니다.


답변

sprintfJavaScript 에 대한 구현을 찾고 있으므로 작은 오류가있는 부동 소수점 (이진 형식으로 저장되어 있기 때문에)을 원하는 형식으로 작성할 수 있습니다.

javascript-sprintf를 시도 하면 다음과 같이 호출됩니다.

var yourString = sprintf("%.2f", yourNumber);

소수점 이하 두 자리의 부동 소수점으로 숫자를 인쇄합니다.

부동 소수점을 주어진 정밀도로 반올림하기 위해 더 많은 파일을 포함하지 않으려는 경우 표시 목적으로 Number.toFixed () 를 사용할 수도 있습니다 .


답변

BigNumber.js 가 내 요구를 충족시키는 것으로 나타 났습니다 .

임의 정밀도 10 진수 및 비소수 산술을위한 JavaScript 라이브러리.

문서 가 훌륭하고 저자는 피드백에 부지런히 응답합니다.

동일한 저자는 2 개의 다른 유사한 라이브러리를 가지고 있습니다 :

Big.js

임의 정밀도 10 진수 산술을위한 작고 빠른 JavaScript 라이브러리입니다. bignumber.js의 여동생.

Decimal.js

JavaScript에 대한 임의 정밀도 10 진수 유형입니다.

BigNumber를 사용하는 코드는 다음과 같습니다.

$(function(){


  var product = BigNumber(.1).times(.2);
  $('#product').text(product);

  var sum = BigNumber(.1).plus(.2);
  $('#sum').text(sum);


});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

<!-- 1.4.1 is not the current version, but works for this example. -->
<script src="http://cdn.bootcss.com/bignumber.js/1.4.1/bignumber.min.js"></script>

.1 &times; .2 = <span id="product"></span><br>
.1 &plus; .2 = <span id="sum"></span><br>


답변

var times = function (a, b) {
    return Math.round((a * b) * 100)/100;
};

—또는—

var fpFix = function (n) {
    return Math.round(n * 100)/100;
};

fpFix(0.1*0.2); // -> 0.02

—또한—

var fpArithmetic = function (op, x, y) {
    var n = {
            '*': x * y,
            '-': x - y,
            '+': x + y,
            '/': x / y
        }[op];

    return Math.round(n * 100)/100;
};

—에서와 같이 —

fpArithmetic('*', 0.1, 0.2);
// 0.02

fpArithmetic('+', 0.1, 0.2);
// 0.3

fpArithmetic('-', 0.1, 0.2);
// -0.1

fpArithmetic('/', 0.2, 0.1);
// 2