이유 : JS는 2에 따라 추가, 뺄셈, 곱셈 및 소수의 분할을 처리합니다. ARG1의 정확도가 확장되거나 반대로 확장되므로 다음 상황이 발생합니다.
JavaScript (JS)의 소수점을 추가, 빼기, 곱셈 및 분할의 문제는 0.3*1 = 0.29999999999 등과 같은 JS의 버그입니다. 다음은 해당 정확도를 완벽하게 계산할 수있는 4 개의 JS 알고리즘을 나열합니다.
함수 accdiv (arg1, arg2) {var t1 = 0, t2 = 0, R1, R2; try {t1 = arg1.tostring (). split ( ".") r2 = 숫자 (arg2.toString (). replace ( ".", ")) return accmul ((r1/r2), pow (10, t2-t1)); }} // 다중 함수 accmul (arg1, arg2) {var m = 0, s1 = arg1.toString (), s2 = arg2.toString (); {m+= s1.split ( ".") AccAdd (arg1, arg2) {var r1, r2, m; try {r1 = arg1.tostring (). split ( ".") } // subtaction function subtr (arg1, arg2) {var r1, r2, m, n; try {r1 = arg1.tostring (). split ( ".") n = (r1> = r2)? r1 : r2; return ((arg1*m-arg2*m)/m) .tofixed (n); }JavaScript의 디지털 정밀도 손실을 분석합시다.
1. JS 디지털 정밀 손실의 일부 일반적인 문제
1. 두 개의 간단한 부동 소수점 번호를 추가하십시오
0.1 + 0.2! = 0.3 // true
방화범
이것은 실제로 파이어 버그 문제가 아니며 경고 (하하, 농담)로 시도 할 수 있습니다.
Java의 계산 결과를 확인하십시오
파이썬을 살펴보십시오
2. 큰 정수 작업
99999999999999999 == 100000000000000000001 //?
방화범
16 자리 및 17 자리 숫자는 실제로 같으며 비합리적입니다.
또한 좋아합니다
var x = 9007199254740992x + 1 == x //?
결과를 참조하십시오
세 가지 조회수가 다시 전복되었습니다.
3. Tofixed는 둥글지 않습니다 (크롬)
1.335.tofixed (2) // 1.33
방화범
Chrome 및 기타 브라우저에는 온라인에서 일관되지 않은 가격이 부족했으며 정확하게는 Tofixed 호환성 문제 때문입니다.
2. JS 수 손실 정확도의 이유
컴퓨터 이진 구현 및 비트 한계에 의해 일부 숫자는 유한하게 표현할 수 없습니다. PI 3.1415926 ..., 1.3333 ... 등과 같이 일부 비이성적 인 숫자를 유한하게 표현할 수없는 것처럼 JS는 IEEE 754 사양을 따르고 이중 정밀 저장을 채택하며 64 비트를 차지합니다. 그림처럼
중요성
부동 소수점 번호 (예 :
0.1 >> 0.0001 1001 1001 1001… (1001 무한 루프) 0.2 >> 0.0011 0011 0011 0011… (0011 Infinite Loop)
현재 소수 시스템을 모방하여 반올림 할 수는 있지만 0과 1의 이진 시스템은 두 가지만 있습니다. 0과 1은 1 씩 반올림합니다. 이것이 컴퓨터의 일부 부동 소수점 숫자가 오류가 있고 정확도가 손실되는 이유입니다.
큰 정수의 정확도 손실은 기본적으로 부동 소수점 번호와 동일합니다. 최대 Mantissa 비트는 52 비트이므로 JS에서 정확하게 표현할 수있는 최대 정수는 Math.pow (2, 53)이며 9007199254740992입니다.
9007199254740992 이상이 정확도를 잃을 수 있습니다
9007199254740992 >> 10000000000000000 ... 000 // 총 530007199254740992 + 1 >> 100000000000000000 ... 001 // 중간 52 0900719254740992 + 2 >> 100000000000000 ... 010 // 010 //
사실은
9007199254740992 + 1 // 손실 된 9007199254740992 + 2 // 손실되지 않음 9007199254740992 + 3 // 손실 된 900719925474740992 + 4 // 손실되지 않음
결과는 그림에 나와 있습니다
위에서 언급했듯이, 우리는 겉보기에 유한 한 숫자가 컴퓨터의 이진 표현에서 무한하다는 것을 알 수 있습니다. 스토리지 비트 한계로 인해 "도망"이 있으며 정확도 손실이 발생합니다.
보다 심층적 인 분석을 위해이 논문 (길고 냄새가 좋은)을 읽을 수 있습니다.
3. 솔루션
정수의 경우 프론트 엔드의 문제 확률이 상대적으로 낮을 수 있습니다. 결국, 초대형 정수를 사용해야하는 비즈니스는 거의 없습니다. 계산 결과가 math.pow (2, 53)를 초과하지 않는 한 정확도는 손실되지 않습니다.
소수의 경우, 프론트 엔드, 특히 금액과 같은 데이터와 관련된 일부 전자 상거래 웹 사이트에서 여전히 많은 문제가 있습니다. 솔루션 : 소수점을 정수 (다중 다중)에 넣은 다음 원래의 배수로 다시 줄입니다 (다중 구분).
// 0.1 + 0.2 (0.1*10 + 0.2*10) / 10 == 0.3 // true
다음은 첨가, 뺄셈, 곱셈 및 소수의 분할의 정확성 상실을 차단하기 위해 쓴 대상입니다. 물론 변환 된 정수는 9007199254740992를 초과 할 수 없습니다.
/*** floatobj에는 부동 소수점 수 계산이 정확도를 잃지 않도록 할 수있는 첨가, 뺄셈, 곱셈 및 분할의 네 가지 방법이 포함되어 있습니다. 근본 원인은 바이너리로 표현할 수없고 비트 한계를 구현할 수 없기 때문입니다.* 다음은 10 진수 소수점* 0.1 >> 0.0001 1001 1001 1001 1001… (1001 무한 루프)* 0.2 >> 0.0011 0011 0011 0011… (0011 Infinite Loop)에 해당하는 이진 표현입니다. 예를 들어, JavaScript는 64 비트 스토리지 숫자 유형을 사용하므로이를 초과하는 숫자 유형은 폐기됩니다. 잃어버린 부분은 정확도로 손실되는 부분입니다. ** ** 메소드 *** 추가 / 빼기 / 배수 / 분할 ** ** 설명 *** 0.1 + 0.2 == 0.300000000000000004 (0.000000000000000 More)* 0.2 + 0.4 == 0.6000000000000001 (0.00000000000000001)* 19.9000 == 198999999999999999999999999999999999999998 (0.00000000000000002) ** floatobj.add (0.1, 0.2) >> 0.3*floatobj.multiply (19.9, 100) >> 1990 **/var floatobj = function () {/** OBJ가 정수*/기능이 IsInteger (obj) {return math point 정수와 정수를 반환합니다. 예를 들어, 3.14 >> 314, 배수는 100* @param floatnum {number} decimal* @return {object}* {times : 100, num : 314}*/function toInteger (floatnum) {var ret = {times : 1, num : 0} if (isinteger (floatnum)) {retnum = varnum = varnum = varnum = varnum = varnum = var} dotpos = strfi.indexof ( '.') var len = strfi.substr (dotpos + 1) .lengthvar times = math.pow (10, len) var intnum = parseint (floatnum* times + 0.5, 10) ret.times = times.num = intnumreturn ret}/** 핵심 방법을 구현하기위한 핵심 방법, 핵심 방법은 후각 상태를 유지하지 못하고 있습니다. 아이디어 : 소수점을 정수 (승수)로 확대하고, 산술 작업을 수행 한 다음, 소수점 (분할) ** @param a {number} 작업 번호 1* @param b {number} 작동 번호 2* @param digits {number} 정확도, 2와 같은 두 십등 점의 숫자 {numb 뺄셈, 곱셈 및 분할 (추가/빼기/곱셈/분할) **/기능 작동 (a, b, 숫자, op) {var o1 = tointeger (a) var o2 = tointeger (b) var n1 = o1.numvar n2 = o2.numvar t1 = o1.timesvar t2 = o2.timesvar max = t1> t2? t1 : t2var result = nullswitch (op) {case 'add': if (t1 === t2) {// 소수점 두 자리가 동일한 결과 = n1 + n2} else if (t1> t2) {// o1 소수점 장소보다 큰 소수점 {t1 / t2) {// o1 decimal place {// o1 decimal place} / t1) + n2} return result / maxcase 'suptract': if (t1 === t2) {result = n1 -n2} else if (t1> t2) {result = n1 -n2 * (t1 / t2)} else {result = n1 * (t2 / t1) - n2} result = result = (n1 * n2) / (t1)). 'divide': result = (n1 / n2) * (t2 / t1) return result}}} // 4 개의 추가 인터페이스, 뺄셈, 곱셈 및 분할 함수 추가 (a, b, digits) {반환 작동 (a, b, 숫자 '}} 함수 subtract (a, b, digits) {숫자 (a, b, digits,') functattly ') 작동 (A, B, Digits, 'Multiply')} 함수 Divide (A, B, Digits) {return Operation (A, B, Digits, 'Divide')} // ExportSreturn {Add : Add : Suxcrate, Duptry : Divide}} (Divide}} ();Tofixed의 수정은 다음과 같습니다
// Tofixed Fix 함수 Tofixed (num, s) {var times = math.pow (10, s) var des = num * times + 0.5des = parseint (des, 10) / timesreturn des + ''}