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


617

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

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

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

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

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


118
실제로 오류는 0.1유한 이진 부동 소수점 숫자 에 매핑 할 수있는 방법이 없기 때문입니다 .
Aaron Digulla

10
대부분의 분수는 정확한 정밀도로 10 진수로 변환 할 수 없습니다. 좋은 설명은 여기에 있습니다 : docs.python.org/release/2.5.1/tut/node16.html
Nate Zaugg


53
@ SalmanA : JavaScript 런타임 이이 문제를 숨기고 있다고해서 내가 틀렸다는 것을 의미하지는 않습니다.
Aaron Digulla

5
Aaron과 의견이 맞지 않으면 0.1을 이진수로 완벽하고 완벽하게 코딩하는 방법이 있습니다. 그러나 IEEE 754가 반드시 이것을 정의하지는 않습니다. 한편으로는 정수 부분을 이진수로, 다른 한편으로는 소수 부분을 최대 n 소수점까지, 이진수로 정규 정수> 0, 마지막으로 소수점 위치를 코딩하는 표현을 상상해보십시오. . 글쎄, 당신은 오류없이 0.1을 완벽하게 나타낼 것입니다. Btw, JS는 내부에 유한 수의 소수점을 사용하기 때문에 개발자는 마지막 소수점에 실수를하지 않도록 내장을 코딩 할 수 있습니다.
Fabien Haddadi

답변:


469

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

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

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

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

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

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


11
BigDecimal에 대한 링크가 끊 겼음을 발견하고 미러를 찾는 동안 BigNumber라는 대안을 찾았습니다. jsfromhell.com/classes/bignumber
Jacksonkr

4
@ bass-t : 예. 그러나 float는 유효 길이까지의 정수를 정확하게 나타낼 수 있으며 ECMA 표준에 따라 64 비트 float입니다. 따라서 정확히 2 ^ 52까지의 정수를 나타낼 수 있습니다.
Michael Borgwardt

5
@Karl : 10 진수 분수 1/10은 기본 2에서 유한 바이너리 분수로 표현 될 수 없으며 이것이 바로 자바 스크립트 숫자입니다. 그래서 입니다 사실 정확히 같은 문제.
Michael Borgwardt

12
정수조차도 자바 스크립트에서 정밀도 문제가 있음을 알게되었습니다. console.log(9332654729891549)실제로 인쇄 한다는 것을 고려하십시오 9332654729891548(즉, 하나씩 인쇄 )
mlathe

12
@mlathe : Doh .. ;P... 2⁵²= 4,503,599,627,370,4962⁵³= 사이 9,007,199,254,740,992에 표현 가능한 숫자는 정확히 정수 입니다. 다음 범위에서 2⁵³까지 2⁵⁴, 모든 것이되어 곱한2 표현할 수있는 숫자는 그래서, 심지어 사람 , 의 이전 범위에 대한 반대로, 2⁵¹2⁵², 간격은 0.5, 기본을 감소 | | 단순히 증가로 인해입니다 기수 64 비트 부동 소수점 값의 2 진 지수 ( 및 ~ toPrecision()사이의 값 에 대해 거의 문서화되지 않은 '예기치 않은'동작을 설명합니다 ). 01
GitaarLAB

126

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

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

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

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


21
12를 선택한 이유는 무엇입니까?
qwertymk

18
toPrecision숫자 대신 문자열을 반환합니다. 항상 바람직한 것은 아닙니다.
SStanley

7
parseFloat (1.005) .toPrecision (3) => 1.00
Peter

5
@ user2428118, 알다시피, 반올림 오류를 표시하려고했습니다. 결과는 1.01 대신 1.00입니다
Peter

9
@ user2428118의 말은 충분하지 않을 수 있습니다. toPrecision은 소수가 아닌 정수를 계산하기 때문에 49.95 대신 (9.99*5).toPrecision(2)= 50 입니다. 그런 다음을 사용할 수 있지만 결과가 100보다 크면 다시 운이 나빠집니다. 왜냐하면 처음 3 개의 숫자와 1 개의 소수점을 허용하므로 점을 이동시키고이를 다소 사용할 수 없게 만듭니다. 나는 대신 사용 했다toPrecision(4)toFixed(2)
aexl

79

수학적으로 기울어 진 경우 : 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 와 같은 테스트 라이브러리를 사용하는 것이 좋습니다.


1
김정일이 우아하고 해결 방법을 사랑하지만 완벽하지 않을 것 같다 jsfiddle.net/Dm6F5/1 Math.a (76.65, 38.45) 반환 115.10000000000002
nicolallias

3
Math.m (10,2332226616)에서 음수 값인 "-19627406800"을 제공하고 있습니다. 상한이 있어야합니다.이 문제의 원인 일 수 있습니다. 제안 해주세요
Shiva Komuravelly

1
이 모든 것이 멋지게 보이지만 어딘가에 실수가있는 것 같습니다.
MrYellow

5
그가 말한 매우 빠른 해결책.
Cozzbie

2
위의 코드를 사용하지 마십시오. 작동하지 않으면 절대 '빠른 해결책'이 아닙니다. 이것은 수학 관련 질문이므로 정확성이 필요합니다.
Drenai

49

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


6
... 소수점을 정확히 얻는 방법 .
line-o

7
0.5 * 0.2 = 0.10; 소수점 이하 2 자리 (또는 그 이하)에서자를 수 있습니다. 그러나이 법을 넘어서는 수학적 의미가있는 숫자는 절대 없을 것입니다.
Nate Zaugg

3
이에 대한 인용이 있습니까? 또한 나누기에 대해서도 마찬가지입니다.
Griffin

3
@NateZaugg 당신은 넘친 소수점을자를 수 없습니다, 당신은 금액을 반올림해야합니다, 2090.5 * 8.61은 17999.205이지만 float에서는 17999.204999999998
Lostfields

3
@Lostfields-당신은 맞습니다! 내 답변을 업데이트했습니다.
Nate Zaugg

29

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

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

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

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

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


4
이것이 가장 깨끗한 해결책이라고 생각합니다. 실제로 결과가 0.02가 아니라면 작은 오류는 무시할 수 있습니다. 중요한 것은 임의의 정밀도가 아니라 숫자가 잘 표시 된다는 것입니다.
Long Ouyang

2
복잡한 계산을 위해서는 Borgwardt의 답변을 확인하십시오.
사용할 수 없음

4
그러나 다시 이것은 yourNumber.toFixed (2)와 정확히 동일한 문자열을 반환합니다.
Robert

27

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>


3
제 생각에는 라이브러리를 사용하는 것이 가장 좋습니다.
Anthony

1
이 링크에서 github.com/MikeMcl/big.js/issues/45 bignumber.js-> financial decimal.js-> Scientific big.js-> ???
V 시리즈

20
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

4
결과적으로 동일한 문제가 발생한다고 생각합니다. 부동 소수점을 반환하므로 반환 값도 "올바르지 않을"가능성이 높습니다.
Gertjan

1
매우 영리하고 유용합니다. +1
Jonatas Walker 2013

18

이 함수는 두 개의 부동 소수점 숫자의 곱에서 필요한 정밀도를 결정하고 적절한 정밀도로 결과를 반환합니다. 그렇지 않지만 우아합니다.

function multFloats(a,b){
  var atens = Math.pow(10,String(a).length - String(a).indexOf('.') - 1), 
      btens = Math.pow(10,String(b).length - String(b).indexOf('.') - 1); 
  return (a * atens) * (b * btens) / (atens * btens); 
}

으. 예, 부동 소수점 수학을 위해 숫자를 문자열로 변환하고 그 답을 제안합시다.
앤드류

16

놀랍게도,이 기능은 다른 기능과 유사하지만 아직 게시되지 않았습니다. Math.round ()에 대한 MDN 웹 문서에서 가져온 것입니다. 간결하고 다양한 정밀도를 허용합니다.

function precisionRound(number, precision) {
  var factor = Math.pow(10, precision);
  return Math.round(number * factor) / factor;
}

console.log (precisionRound (1234.5678, 1)); // 예상 출력 : 1234.6

console.log (precisionRound (1234.5678, -1)); // 예상 출력 : 1230

var inp = document.querySelectorAll('input');
var btn = document.querySelector('button');

btn.onclick = function(){
  inp[2].value = precisionRound( parseFloat(inp[0].value) * parseFloat(inp[1].value) , 5 );
};

//MDN function
function precisionRound(number, precision) {
  var factor = Math.pow(10, precision);
  return Math.round(number * factor) / factor;
}
button{
display: block;
}
<input type='text' value='0.1'>
<input type='text' value='0.2'>
<button>Get Product</button>
<input type='text'>

업데이트 : 2019 년 8 월 20 일이 오류가 나타났습니다. Math.round ()의 부동 소수점 정밀도 오류 때문이라고 생각합니다.

precisionRound(1.005, 2) // produces 1, incorrect, should be 1.01

이러한 조건은 올바르게 작동합니다.

precisionRound(0.005, 2) // produces 0.01
precisionRound(1.0005, 3) // produces 1.001
precisionRound(1234.5, 0) // produces 1235
precisionRound(1234.5, -1) // produces 1230

고치다:

function precisionRoundMod(number, precision) {
  var factor = Math.pow(10, precision);
  var n = precision < 0 ? number : 0.01 / factor + number;
  return Math.round( n * factor) / factor;
}

소수 자릿수를 올릴 때 오른쪽에 숫자가 추가됩니다. MDN은 Math.round 페이지를 업데이트하여 더 나은 솔루션을 제공 할 수 있습니다.


잘못된 답변. 10.2는 항상 10.19를 반환합니다. jsbin.com/tozogiwide/edit?html,js
콘솔,

@ Žilvinas 게시 한 JSBin 링크는 위에 나열된 MDN 기능을 사용하지 않습니다. 귀하의 의견은 잘못된 사람을 향한 것 같습니다.
HelloWorldPeace

13

당신은 당신이 실제로 원하는 소수 자릿수에 대해 마음을 정해야합니다-케이크를 먹을 수 없으며 너무 먹습니다 :-)

추가 오류가 발생할 때마다 수치 오류가 누적되며 일찍 중단하지 않으면 커질 것입니다. 깨끗하게 보이는 결과를 제공하는 숫자 라이브러리는 모든 단계에서 마지막 2 자리를 간단히 잘라냅니다. 숫자 보조 프로세서도 같은 이유로 "일반"및 "전체"길이를 갖습니다. Cuf-off는 프로세서에서는 저렴하지만 스크립트에서는 pov (...)를 곱하고 나누고 사용하는 것이 매우 비쌉니다. 좋은 수학 라이브러리는 floor (x, n)을 제공하여 컷오프를 수행합니다.

따라서 최소한 pov (10, n)을 사용하여 전역 var / constant를 만들어야합니다. 즉, 필요한 정밀도를 결정했음을 의미합니다.

Math.floor(x*PREC_LIM)/PREC_LIM  // floor - you are cutting off, not rounding

결과를 표시하고 if-s를 수행하지 않는다고 가정하면 마지막에 수학을 계속하고 끝낼 수 있습니다. 그렇게 할 수 있다면 .toFixed (...)가 더 효율적일 수 있습니다.

if-s / comparisons를하고 있고 잘라 내기를 원하지 않는다면 보통 eps라고 불리는 작은 상수가 필요합니다.이 상수는 최대 예상 오차보다 소수점 이하 1 자리입니다. 컷오프가 소수점 이하 두 자릿수라고 가정하면 eps는 마지막 3 번째 자리 (1 번째 중요도)에서 1을 가지며 결과를 예상 eps 범위 (0.02 -eps <0.1) 내에 있는지 비교하는 데 사용할 수 있습니다. * 0.2 <0.02 + eps).


가난한 사람의 반올림을하기 위해 0.5를 추가 할 수도 있습니다 : Math.floor (x * PREC_LIM + 0.5) / PREC_LIM
cmroanirgo

그러나 예를 들어 Math.floor(-2.1)입니다 -3. 아마 예를 들어Math[x<0?'ceil':'floor'](x*PREC_LIM)/PREC_LIM
MikeM

floor대신에 round?
Quinn Comendant

12

당신은 사용 parseFloat()하고 toFixed()당신이 작은 작업에이 문제를 우회하려는 경우 :

a = 0.1;
b = 0.2;

a + b = 0.30000000000000004;

c = parseFloat((a+b).toFixed(2));

c = 0.3;

a = 0.3;
b = 0.2;

a - b = 0.09999999999999998;

c = parseFloat((a-b).toFixed(2));

c = 0.1;

11

phpjs.org의 round () 함수는 잘 작동합니다 : http://phpjs.org/functions/round

num = .01 + .06;  // yields 0.0699999999999
rnum = round(num,12); // yields 0.07

2
@jrg 규칙에 따라 "5"로 끝나는 숫자는 가장 가까운 짝수로 반올림됩니다 (항상 반올림 또는 내림하면 결과에 치우침이 생길 수 있습니다). 따라서 소수점 이하 두 자리로 반올림 한 4.725는 실제로 4.72 여야합니다.
Mark A. Durham

9

0.6 * 3 대단합니다!)) 나에게 이것은 잘 작동합니다.

function dec( num )
{
    var p = 100;
    return Math.round( num * p ) / p;
}

매우 간단합니다))


그래도 이런 식으로 작동 8.22e-8 * 1.3합니까?
Paul Carlton

0.6 x 3 = 1.8, 결과를 2로 제공하는 코드는 좋지 않습니다.
Zyo

@Zyo이 경우 1.8을 반환합니다. 어떻게 운영 했습니까?
Drenai

흥미 롭군 곱셈과 나눗셈 연산자를 바꿀 수 있으며 작동합니다.
앤드류

9

일반적인 용도로는이 동작이 허용 될 수 있습니다.
부동 소수점 값을 비교하여 적절한 조치를 결정할 때 문제가 발생합니다.
ES6의 출현으로 Number.EPSILON허용 가능한 오차 한계를 결정하기 위해 새로운 상수 가 정의됩니다.
따라서 이와 같은 비교를 수행하는 대신

0.1 + 0.2 === 0.3 // which returns false

다음과 같이 사용자 정의 비교 기능을 정의 할 수 있습니다.

function epsEqu(x, y) {
    return Math.abs(x - y) < Number.EPSILON;
}
console.log(epsEqu(0.1+0.2, 0.3)); // true

출처 : http://2ality.com/2015/04/numbers-math-es6.html#numberepsilon


제 경우에는 Number.EPSILON이 너무 작아서 다음과 같은 결과가 나왔습니다.0.9 !== 0.8999999761581421
Tom

8

얻은 결과는 다른 언어, 프로세서 및 운영 체제의 부동 소수점 구현에서 정확하고 일관됩니다. 부동 소수점이 실제로 두 배 이상인 경우 부정확성 수준 만 변경됩니다.

이진 부동 소수점의 0.1은 소수점의 1/3과 같습니다 (즉, 0.3333333333333 ... 영원히). 그것을 처리하는 정확한 방법은 없습니다.

항상 수레를 다루는 경우 작은 반올림 오류가 예상되므로 항상 표시된 결과를 적절한 것으로 반올림해야합니다. 그 대가로 모든 계산이 프로세서의 기본 바이너리에 있기 때문에 매우 빠르고 강력한 산술이 가능합니다.

대부분의 경우 솔루션은 고정 소수점 산술로 전환하지 않습니다. 주로 정확도가 필요하지 않은 시간의 99 %가 훨씬 느리기 때문입니다. 금융 거래와 같이 정확도가 필요한 것들을 다루는 경우 Javascript는 어쨌든 사용하기에 가장 좋은 도구가 아닙니다 (고정 소수점 유형을 적용하려는 경우 정적 언어가 더 낫습니다) ).

우아한 솔루션을 찾고 있다면 이것이 두렵습니다. 수레는 빠르지 만 작은 반올림 오류가 있습니다-결과를 표시 할 때 항상 합리적인 것으로 반올림하십시오.


8

이를 피하려면 부동 소수점 대신 정수 값을 사용해야합니다. 따라서 * 100의 값으로 2 개의 위치에서 정밀하게 작업하려면 3 개의 위치에 1000을 사용하십시오. 표시 할 때는 포맷터를 사용하여 구분 기호를 사용하십시오.

많은 시스템에서 이런 식으로 소수 자릿수를 생략합니다. 이것이 많은 시스템이 달러 / 유로 대신 부동 소수점으로 센트 (정수)로 작동하는 이유입니다.


7

문제

부동 소수점은 모든 소수 값을 정확하게 저장할 수 없습니다. 따라서 부동 소수점 형식을 사용하면 입력 값에 항상 반올림 오류가 발생합니다. 물론 입력의 오류는 출력의 오류를 초래합니다. 불연속 기능 또는 작동 자의 경우 기능 또는 작동자가 불연속 인 지점 주변에서 출력에 큰 차이가있을 수 있습니다.

부동 소수점 값의 입력 및 출력

따라서 부동 소수점 변수를 사용할 때는 항상이를 알고 있어야합니다. 부동 소수점을 사용한 계산에서 원하는 출력은 항상이를 염두에두고 표시하기 전에 형식화 / 조건화되어야합니다.
연속 함수와 연산자 만 사용하는 경우 원하는 정밀도로 반올림하는 경우가 많습니다 (잘리지 않음). float를 문자열로 변환하는 데 사용되는 표준 형식 지정 기능이 일반적으로이 작업을 수행합니다.
반올림으로 인해 전체 오차가 원하는 정밀도의 절반 이상이 될 수있는 오차가 발생하므로 예상되는 입력 정밀도와 원하는 출력 정밀도를 기반으로 출력을 수정해야합니다. 당신은해야

  • 입력을 예상 정밀도로 반올림하거나 더 높은 정밀도로 값을 입력 할 수 없도록합니다.
  • 반올림 / 포맷하기 전에 출력에 작은 값을 추가하여 원하는 정밀도의 1/4보다 작거나 같고 입력 및 계산 중 반올림 오차로 인한 최대 예상 오차보다 큽니다. 이것이 가능하지 않은 경우 사용 된 데이터 유형의 정밀도 조합이 계산에 원하는 출력 정밀도를 제공하기에 충분하지 않습니다.

이 두 가지 작업은 일반적으로 수행되지 않으며 대부분의 경우 이러한 작업을 수행하지 않아 발생하는 차이가 너무 작아 대부분의 사용자에게 중요하지는 않지만 이미 해당 수정없이 사용자가 출력을 수락하지 않은 프로젝트가있었습니다.

이산 함수 또는 연산자 (모듈라와 같은)

개별 연산자 또는 기능이 관련된 경우 출력이 예상대로 이루어 지도록 추가 수정이 필요할 수 있습니다. 반올림 전에 반올림 및 작은 수정을 추가해도 문제를 해결할 수 없습니다.
이산 함수 또는 연산자를 적용한 직후에 중간 계산 결과에 대한 특별한 점검 / 수정이 필요할 수 있습니다. 특정 경우 ( 모듈러스 연산자) 에 대한 질문에 대한 대답 은 모듈러스 연산자가 Javascript에서 소수를 반환하는 이유는 무엇입니까?를 참조하십시오.

더 나은 문제를 피하십시오

반올림 오류없이 예상 입력을 저장할 수있는 이와 같은 계산에 데이터 유형 (정수 또는 고정 소수점 형식)을 사용하여 이러한 문제를 피하는 것이 종종 더 효율적입니다. 그 예로 금융 계산에 부동 소수점 값을 사용해서는 안됩니다.


4

고정 소수점 산술을 살펴보십시오 . 조작하려는 숫자 범위가 작은 경우 (예 : 통화) 문제를 해결할 수 있습니다. 소수의 소수 값으로 반올림하여 가장 간단한 솔루션입니다.


5
문제는 부동 소수점 대 고정 소수점이 아니며 이진 대 십진수입니다.
Michael Borgwardt



4

여기에 이미지 설명을 입력하십시오

    You can use library https://github.com/MikeMcl/decimal.js/. 
    it will   help  lot to give proper solution. 
    javascript console output 95 *722228.630 /100 = 686117.1984999999
    decimal library implementation 
    var firstNumber = new Decimal(95);
    var secondNumber = new Decimal(722228.630);
    var thirdNumber = new Decimal(100);
    var partialOutput = firstNumber.times(secondNumber);
    console.log(partialOutput);
    var output = new Decimal(partialOutput).div(thirdNumber);
    alert(output.valueOf());
    console.log(output.valueOf())== 686117.1985

3

당신이 맞습니다. 그 이유는 부동 소수점 숫자의 정밀도가 제한되어 있기 때문입니다. 유리수를 두 정수로 나눈 값을 저장하면 대부분의 경우 정밀한 손실없이 숫자를 저장할 수 있습니다. 인쇄 할 때 결과를 분수로 표시 할 수 있습니다. 내가 제안한 표현으로, 그것은 사소한 것이된다.

물론 그것은 비이성적 인 숫자에는별로 도움이되지 않습니다. 그러나 가장 적은 문제 (예 :와 같은 상황 감지)를 유발하는 방식으로 계산을 최적화 할 수 있습니다 sqrt(3)^2).


자네 말이 맞아, 그 이유는 부동 소수점 수의 제한 정밀도입니다 - <pedant>사실, 영업 이익은 잘못이다, 부정확 한 부동 소수점 연산에 내려 놓고</pedant>
detly

3

mod 3에서 불쾌한 반올림 오류 문제가 발생했습니다. 때로는 0을 받아야 할 때 .000 ... 01을 얻습니다. <= .01을 테스트하기 만하면됩니다. 그러나 때로는 2.99999999999998을 얻습니다. 아야!

BigNumbers 는이 문제를 해결했지만 다소 아이러니 한 또 다른 문제를 소개했습니다. BigNumbers에 8.5를로드하려고 할 때 실제로는 8.4999이고 15 자리 이상의 유효 숫자를 가졌다는 알림을 받았습니다. 이것은 BigNumbers가 그것을 받아 들일 수 없다는 것을 의미했습니다 (나는이 문제가 다소 역설적이라고 언급했습니다).

아이러니 한 문제에 대한 간단한 해결책 :

x = Math.round(x*100);
// I only need 2 decimal places, if i needed 3 I would use 1,000, etc.
x = x / 100;
xB = new BigNumber(x);

2

사용 번호 (1.234443) .toFixed (2); 1.23을 인쇄합니다

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

2

decimal.js , big.js 또는 bignumber.js 를 사용하여 Javascript에서 부동 소수점 조작 문제를 피할 수 있습니다.

0.1 * 0.2                                // 0.020000000000000004
x = new Decimal(0.1)
y = x.times(0.2)                          // '0.2'
x.times(0.2).equals(0.2)                  // true

big.js : 미니멀리스트; 사용하기 쉬운; 소수점 이하 자릿수로 지정된 정밀도; 분할에만 적용되는 정밀도.

bignumber.js : 염기 2-64; 구성 옵션; NaN; 무한대; 소수점 이하 자릿수로 지정된 정밀도; 분할에만 적용되는 정밀도; 기본 접두사.

decimal.js :베이스 2-64; 구성 옵션; NaN; 무한대; 정수가 아닌 제곱, exp, ln, log; 유효 숫자로 지정된 정밀도; 항상 적용되는 정밀도; 난수.

자세한 비교 링크


2

우아하고 예측 가능하며 재사용 가능

재사용 가능한 방식으로 문제를 처리해 봅시다. 다음 7 줄 .decimal을 사용하면 숫자, 수식 또는 내장 Math함수 의 끝에 추가 하여 원하는 수의 부동 소수점 정밀도에 액세스 할 수 있습니다.

// First extend the native Number object to handle precision. This populates
// the functionality to all math operations.

Object.defineProperty(Number.prototype, "decimal", {
  get: function decimal() {
    Number.precision = "precision" in Number ? Number.precision : 3;
    var f = Math.pow(10, Number.precision);
    return Math.round( this * f ) / f;
  }
});


// Now lets see how it works by adjusting our global precision level and 
// checking our results.

console.log("'1/3 + 1/3 + 1/3 = 1' Right?");
console.log((0.3333 + 0.3333 + 0.3333).decimal == 1); // true

console.log(0.3333.decimal); // 0.333 - A raw 4 digit decimal, trimmed to 3...

Number.precision = 3;
console.log("Precision: 3");
console.log((0.8 + 0.2).decimal); // 1
console.log((0.08 + 0.02).decimal); // 0.1
console.log((0.008 + 0.002).decimal); // 0.01
console.log((0.0008 + 0.0002).decimal); // 0.001

Number.precision = 2;
console.log("Precision: 2");
console.log((0.8 + 0.2).decimal); // 1
console.log((0.08 + 0.02).decimal); // 0.1
console.log((0.008 + 0.002).decimal); // 0.01
console.log((0.0008 + 0.0002).decimal); // 0

Number.precision = 1;
console.log("Precision: 1");
console.log((0.8 + 0.2).decimal); // 1
console.log((0.08 + 0.02).decimal); // 0.1
console.log((0.008 + 0.002).decimal); // 0
console.log((0.0008 + 0.0002).decimal); // 0

Number.precision = 0;
console.log("Precision: 0");
console.log((0.8 + 0.2).decimal); // 1
console.log((0.08 + 0.02).decimal); // 0
console.log((0.008 + 0.002).decimal); // 0
console.log((0.0008 + 0.0002).decimal); // 0

건배!


2
downvote를 선택한 경우 최소한 이유를 제공하십시오.
Bernesto

1

사용하다

var x = 0.1*0.2;
 x =Math.round(x*Math.pow(10,2))/Math.pow(10,2);

4
흠 ...하지만 참고로 항상 소수점 2 자리로 반올림합니다. 그것은 물론 옵션이지만 0.55 * 0.55 계산은 어떻습니까 (정확한 숫자를 미리 알지 못하기 때문에 0.3025 대신 0.3을 줄 것입니다. 물론 사용할 수 있습니다 Math.round(x*Math.pow(10,4))/Math.pow(10,4);. 반올림은 항상 옵션입니다. 더 좋은 해결책이 있는지 알고 싶었습니다
Juri

10.2는 항상 10.19를 반환합니다. jsbin.com/tozogiwide/edit?html,js,console,output
Žilvinas


1

이것은 나를 위해 작동합니다 :

function round_up( value, precision ) { 
    var pow = Math.pow ( 10, precision ); 
    return ( Math.ceil ( pow * value ) + Math.ceil ( pow * value - Math.ceil ( pow * value ) ) ) / pow; 
}

round_up(341.536, 2); // 341.54

1
불행히도 round_up (4.15,2) => 4.16.
jrg

1

다음 기능을 사용한 출력 :

var toFixedCurrency = function(num){
    var num = (num).toString();
    var one = new RegExp(/\.\d{1}$/).test(num);
    var two = new RegExp(/\.\d{2,}/).test(num);
    var result = null;

    if(one){ result = num.replace(/\.(\d{1})$/, '.$10');
    } else if(two){ result = num.replace(/\.(\d{2})\d*/, '.$1');
    } else { result = num*100; }

    return result;
}

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

test();

출력에주의하십시오 toFixedCurrency(x).

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.