자바 스크립트는 부동 소수점 디지털 오류로 인한 문제를 기억

수요

제품 생산 후 근로자 쇼핑 초기 자체 테스트를 완료하고, 전화로보고했다. 실제 생산에 따라서 입력 값과의 사용자 (작업자) 불편한 값을 선택하기위한 선택기 모드 설계된 몇몇 아이템을 형성한다. 값의 범위는, 상기 허용 오차 범위에 따라 발생. 다음 예는 다음과 같다 :

示例一
// 误差
0.01mm ~ 0.06mm
// picker 展示的数值
0.01, 0.02, 0.03, 0.04, 0.05, 0.06

示例二
// 误差
15mm ~ 18mm
// picker 展示的数值
15, 16, 17, 18

示例三
// 误差
1.05mm ~ 1.1mm
// picker 展示的数值
1.05, 1.06, 1.07, 1.08, 1.09, 1.1

상기 예에서 알 수 있고, 범위는베이스와 오차 범위의 최소값의 자리수의 최소 수에 따라 계산되며, 점진적으로 최대 (포함) 내지 (포함)의 최소 값에서 축적된다.

실현

먼저, 소수점 수에 따른 최소값.

function getDecimalPlace(value) {
    // 先将 Number 转换为 String
    value = value + '';
    // 查找小数点的位置,加 1 是为了方便计算小数点的位数。
    var floatIndex = value.indexOf('.') + 1;
    // 返回的结果是小数位的个数
    return floatIndex ? value.length - floatIndex : 0;
}

여러 실질적인 값이 시험 방법.

getDecimalPlace(1); //0
getDecimalPlace('1.0'); //0
getDecimalPlace('1.5'); //1
getDecimalPlace('1.23'); //2

그런 다음, 숫자 계산 기본 소수점을 축적.

var min = 0.01;
var max = 0.06;

var decimal = getDecimalPlace(min);
// 基数
var radixValue = Math.pow(10, -decimal);

마지막으로, 오차 범위 및 기재에 기초하여,주기 범위를 생성한다.

var value = min;
var range = [];

for (; value <= max; value += radixValue) {
    range.push(value);
}
console.log(range);
//结果:[0.01,0.02,0.03,0.04,0.05]

잘못 보이는 결과에서. 예, 0.06의 최대 값은 값 범위에 표시되지 않습니다.

문제

자바 스크립트는 IEEE-754 부동 소수점 표현을 사용합니다. 이것은 진수 이진 부동 소수점 표현하고 정확하게 0.1 간단한 숫자로 표현 될 수없는 것과 같다.

간단한 예함으로써 상기 단락을 확인한다.

var num1 = 0.2 - 0.1;
var num2 = 0.3 - 0.2;
console.log(num1 === num2); //false
console.log(num1 === 0.1); //true
console.log(num2 === 0.1); //false

또, 앞의 범위의 계산에있어서, 본 유사한 문제가 발생 될 수있다.

var max = 0.06;
var value = 0.05;
console.log(value + 0.01 === max); //false

때문에 0.05 + 0.01결과 동일하지 않은 0.06루프합니다 (6 번 예상 대신에), 다섯 번 수행 이상이다.

문제를 해결하기 위해 시도하기 전에 코드의 첫 번째 패키지의 전면 봐.

function getRange(min, max) {
    var decimal = getDecimalPlace(min);
    var radixValue = Math.pow(10, -decimal);

    var value = min;
    var range = [];

    for (; value <= max; value += radixValue) {
        range.push(value);
    }
    
    return range;
}

문제 해결

가장 간단한 방법은 미정 상태 순환 조정하는, 최대 값은 사이클 종료 후 어레이에 추가된다.

function getRange(min, max) {
    var decimal = getDecimalPlace(min);
    var radixValue = Math.pow(10, -decimal);

    var value = min;
    var range = [];

    for (; value < max; value += radixValue) {
        range.push(value);
    }
    range.push(max);
    
    return range;
}

다시 테스트 데이터를 사용하기 전에 :

getRange(0.01, 0.06);
//结果:[0.01,0.02,0.03,0.04,0.05,0.06]

기대에 부합하는 결과를 운영, 문제는 해결된다.

새로운 문제

그러나, 테스트를 위로 따라 사고가 있었다.

getRange(1.55, 1.65);
// 结果:[1.55,1.56,1.57,1.58,1.59,1.6,1.61,1.62,1.6300000000000001,1.6400000000000001,1.65]

1.6300000000000001이 값은 우리가 받게 될 것을 분명히 아니다. 이 문제는 이전에 문제의 원인과 일치한다.

옵션 하나

값의 계산에 관련된 첫 번째 정수로 변환 한 후 계산된다.

function getRange(min, max) {
    var decimal = getDecimalPlace(min);
    var radixValue = Math.pow(10, -decimal);

    var multi = Math.pow(10, decimal)

    var value = min * multi;
    var range = [];

    for (; value < max * multi; value += radixValue * multi) {
        range.push(value / multi);
    }
    range.push(max);

    return range;
}

주의 사항 :

  • 배열에 값을 추가하는 경우, 그 최종 값을 구해야 배수로 나눈.

옵션 II

은 Using toFixed()부동 소수점 형식의 방법을.

function getRange(min, max) {
    var decimal = getDecimalPlace(min);
    var radixValue = Math.pow(10, -decimal);

    var value = min;
    var range = [];

    for (; value < max || +value.toFixed(decimal) === max; value += radixValue) {
        range.push(+value.toFixed(decimal));
    }

    return range;
}

주의 사항 :

  • 에서는 toFixed 메서드가 반환 String 형의 값을 ()하고, 따라서 형 숫자로 변환 할 필요가있다.
  • 외부 루프 푸시 () 최대 문장을, 약간의 최적화, 조정 루프 조건을 수행 제거합니다.

최종적으로

자바 스크립트 부동 소수점 정밀도의 오류,하지만 매우 기본적이지만 종종 문제의 심각하지 않다. 기사 공유 프로젝트의 모든 경우를 커버하기에 충분 프로그램,하지만 다른 장소 또는 프로젝트에 사용되는 경우, 일부 극단적 인 경우에 문제가있을 수 있습니다.

참조 문서

추천

출처www.cnblogs.com/xiaoyucoding/p/11976237.html