sitelink1 | http://ponyozzang.tistory.com/60 |
---|---|
sitelink2 | http://gafani.tistory.com/entry/Javascri...8%EA%B8%B0 |
sitelink3 | |
sitelink4 | |
extra_vars4 | |
extra_vars5 | |
extra_vars6 |
・부동소수점
부동소수점으로 작성된 값을 부동소수점 리터럴이라고 부릅니다. 이번에는 부동소수점 리터럴을 스크립트 안에서 작성하는 방법을 확인해보겠습니다.
소수점 형식의 경우는 정수 부분과 소수점 부분을 마침표를 사용해 구분해서 작성합니다.
3.14 0.0014 |
그리고 지수 형식의 경우에는 기수가 10으로써 다음과 같이 작성할 수 있습니다.
3.2e2 2.4e-3 |
[3.2e2]은 [3.2 x 10 2]으로 [320.0]입니다. 그리고 [2.4e-3]은 [2.4 x 10 -3]으로 [0.0024]입니다.
그리고 [e] 대신에 [E]를 사용해도 괜찮습니다.
3.2E2 2.4E-3 |
[Javascript] 소수점 자리수 처리하기
소수점 8자리 수끼리 합산을 했는데, 소수점 14자리가 나왔습니다.
모든 수가 그런것이 아니라, 어느 시점에서 그렇게 변하였는데, 간단히 예제를 보자면:
35.27387426 "+" 1.79806217 "=" 37.07193643
37.07193643 "+" 12.00537839 "=" 49.07731482
49.07731482 "+" 0.66331486 "=" 49.74062968
49.74062968 "+" 9.04520268 "=" 58.78583236
58.78583236 "+" 1.20602704 "=" 59.9918594
59.9918594 "+" 1.20602702 "=" 61.19788642
61.19788642 "+" 0.60301351 "=" 61.80089993
61.80089993 "+" 2.14995101 "=" 63.95085094
63.95085094 "+" 1.18407028 "=" 65.13492122
65.13492122 "+" 1.18407028 "=" 66.3189915
66.3189915 "+" 13.83537715 "=" 80.15436865
80.15436865 "+" 0.59203514 "=" 80.74640378999999
80.74640379 "+" 9.85853479 "=" 90.60493858000001
90.60493858 "+" 9.39506142 "=" 100
위에서 보면 아래쪽 부분에서 부터 8자리끼리 합산하게 되면 소수점이 변경되게 되는데요. 이것은 버그가 아닙니다. 일반적으로 알려진 이슈인데요. 실수 값을 계산할 때 고정된 정밀도를 가지고 계산하기 때문입니다.
그러니까 왜?
자세히 설명하자면, IEEE 754 표준 2진 부동 소수점을 계산하는 모든 프로그래밍 언어들에게서 나타나는 이슈인데요. 자바스크립트는 64-bits 부동 소수점 표현법을 사용하는데, 이것은 Java의 double
과 같아요. 이 문제의 요점은 숫자가 2의 제곱으로 표현된다는 부분에 있어요. 분모가 2의 제곱이 아닌 유리수 (0.1
1/10
과 같은)는 정확하게 표현이 될 수 없기 때문예요.
0.1
을 표준 binary64
형식으로 다음과 같이 정확하게 표현할 수 있어요.
- 10진법으로
0.1000000000000000055511151231257827021181583404541015625
- C99 hexfloat notation 으로
0x1.999999999999ap-4
반대로, 유리수 0.1
과 동일한 1/10
은 다음과 같이 정확히 표현할 수 있어요.
- 10진법으로
0.1
- C99 hexfloat noation 으로
0x1.99999999999999...p-4
이 설명을 쉽게 이해하려면, 다음과 같은 코드를 보시면 이해가 되실 거예요.
> 0.1 + 0.2 == 0.3
< false
> 0.1 + 0.2 === 0.3
< false
이해가 잘 안가신다구요? 그럼 다음의 예제를 보시면 좀 더 이해하기 쉬우실 거예요.
> 1/10 + 2/10 == 3/10
< false
> 1/8 + 2/8 == 3/8
< true
그러면 이해하셨다고 하고, 해결법을 찾아보니 다음과 같이 간단하게 처리가 되었어요.
> 80.74640378999999.toFixed(8)
< "80.74640379"
Number.prototype.toFixed()
toFixed()
메서드는 고정 소수점 표현법을 사용하여 숫자 형식을 지정해요.
문법
numObj.toFixed([digits])
인자들
digits 부가적임. 숫자의 소수점을 표현하고 싶은 자리수. 0 ~ 20까지 가능하고요, 이 값을 비워놓으면 0으로 처리해요.
리턴값
고정 소수점으로 표현된 String을 줘요.
예외처리
RangeError digits 가 너무 크거나 작을 때.
TypeError 이 메서드를 호출한 객체가 Number의 객체가 아닐 때.
설명
toFixed()
는 지수 표기법을 사용하지 않고 소수점 뒤에 정확한 자리수를 가지는 숫자 객체의 문자열을 리턴해요. 필요한 경우에 숫자는 반올림이 되고, 소수 부분에는 필요에 따라서 0으로 채워져 지정된 길이가 되요. 숫자 객체가 1e + 21
보다 큰 경우에 이 메서드는 단순히 Number.prototype.toString()
을 호출하고 지수 표기법의 문자열을 리턴해요.
예제들
var numObj = 12345.6789;
numObj.toFixed(); // Returns '12346': 소수점 전체를 반올림
numObj.toFixed(1); // Returns '12345.7': 반올림
numObj.toFixed(6); // Returns '12345.678900': 모자란 부분은 0으로 채움
(1.23e+20).toFixed(2); // Returns '123000000000000000000.00'
(1.23e-10).toFixed(2); // Returns '0.00'
2.34.toFixed(1); // Returns '2.3'
2.35.toFixed(1); // Returns '2.4'. 이 경우에 반올림됨을 기억하세요.
-2.34.toFixed(1); // Returns -2.3 (연산자 우선순위 때문에 음수 리터럴은 문자열을 리턴하지 않아요.)
(-2.34).toFixed(1); // Returns '-2.3' (괄호를 사용하지 않는다면...)
참조
- Stackoverflow - Is floating point math broken? “80.74640379”)
- Stackoverflow - Javascript adding decimal numbers issue
- MDN - toFixed
Floating-point cheat sheet for JavaScript
브라우저 개발자 콘솔에서 console.debug(0.1*0.2) 를 출력해보면 0.020000000000000004 라고 출력된다
이는 부동 소수점 연산 오류이다
기본 개념과 심화학습 그리고 해결방안은 다음의 사이트에서 학습 가능하다 -> http://floating-point-gui.de/languages/javascript/
일부 내용을 가져온다면
Floating-point cheat sheet for JavaScript
Floating-Point Types
JavaScript is dynamically typed and will often convert implicitly between strings and floating-point numbers (which are IEEE 64 bit values). To force a variable to floating-point, use the global parseFloat()
function.
var num = parseFloat("3.5");
Decimal Types
The oldest decimal type for JavaScript seems to be a port of Java’s BigDecimal
class, which also supports rounding modes:
var a = new BigDecimal("0.01");
var b = new BigDecimal("0.02");
var c = a.add(b); // 0.03
var d = c.setScale(1, BigDecimal.prototype.ROUND_HALF_UP);
There is also bignumber.js
, which is smaller and faster:
BigNumber.config({ROUNDING_MODE: BigNumber.ROUND_HALF_UP})
var a = new BigNumber("0.01");
var b = new BigNumber("0.02");
var c = a.plus(b); // BigNumber(0.03)
var d = c.toFixed(1); // "0.0"
How to Round
var num = 5.123456;
num.toPrecision(1) //returns 5 as string
num.toPrecision(2) //returns 5.1 as string
num.toPrecision(4) //returns 5.123 as string
Using a specific rounding mode:
new BigDecimal("1.25").setScale(1, BigDecimal.prototype.ROUND_HALF_UP);
Resources
여기서 언급된 오픈소스의 javascript 라이브러리들이 있다
1. https://github.com/dtrebbien/BigDecimal.js
2. https://github.com/iriscouch/bigdecimal.js
3. https://github.com/MikeMcl/bignumber.js
4. https://github.com/MikeMcl/big.js
현재(2018.03.26) 기준 라이브러리 버전들은 첨부파일로 업로드해둔다
dtrebbien_BigDecimal.js-master_20180326.zip
iriscouch_bigdecimal.js-master_20180326.zip
mikemcl_big.js-master_20180326.zip
mikemcl_bignumber.js-master_20180326.zip