Js lose precision floating-point calculation

Calculation of commodity prices in the project when it came again js floating-point calculation error occurs, a previously encountered this problem is treated with about tofixed method, which for a programmer and is not rigorous.

Moreover, with tofixed method is also problematic, ways to solve before the reference blog post: https://blog.csdn.net/qq_33237207/article/details/81668457

1, found the problem:

When arithmetic calculations js float test, they are likely to arise, as follows:

console.log(0.1 + 0.2);//0.30000000000000004
console.log(1.0 - 0.9);//0.09999999999999998
console.log(19.9 * 100);//1989.9999999999998
console.log(6.6 / 0.2);//32.99999999999999

2, not precise reasons:

Here we look for the cause of the error floating point operations take :( 0.1 + 0.2 = 0.30000000000000004 for example)

First of all, we want to stand in the perspective of the computer thinking 0.1 + 0.2 this seemingly pediatric problem. We know that the computer can understand the binary rather than decimal, so we put 0.1 and 0.2 into a binary look:

0.1 => 0.0001 100110011001 ... (infinite loop)

0.2 => 001100110011 ... 0.0011 (infinite loop)

We found that after 0.1 and 0.2 above is converted to binary, into a digital infinite loop, which in real life, we can understand an infinite loop, but the computer is not allowed infinite loop, infinite loop for decimal, the computer will be rounding. Double precision floating-point fractional part of the support 52 up, so the two series of so obtained after adding 0.0100 1,100,110,011,001,100 1,100,110,011,001,100 1,100,110,011,001,100, due to the limitations of the floating-point decimal places truncated binary numbers, At this time, we then convert it to a decimal, it becomes .30000000000000004.

Note: conversion between decimal and binary, refer to this article: Decimal and binary conversion

3, the solution

var floatObj = function () {
 
        /*
         * 判断obj是否为一个整数
         */
        function isInteger(obj) {
            return Math.floor(obj) === obj
        }
 
        /*
         * 将一个浮点数转成整数,返回整数和倍数。如 3.14 >> 314,倍数是 100
         * @param floatNum {number} 小数
         * @return {object}
         *   {times:100, num: 314}
         */
        function toInteger(floatNum) {
            var ret = {times: 1, num: 0};
            if (isInteger(floatNum)) {
                ret.num = floatNum;
                return ret
            }
            var strfi = floatNum + '';
            var dotPos = strfi.indexOf('.');
            var len = strfi.substr(dotPos + 1).length;
            var times = Math.pow(10, len);
            var intNum = parseInt(floatNum * times + 0.5, 10);
            ret.times = times;
            ret.num = intNum;
            return ret
        }
 
        /*
         * 核心方法,实现加减乘除运算,确保不丢失精度
         * 思路:把小数放大为整数(乘),进行算术运算,再缩小为小数(除)
         *
         * @param a {number} 运算数1
         * @param b {number} 运算数2
         * @param op {string} 运算类型,有加减乘除(add/subtract/multiply/divide)
         *
         */
        function operation(a, b, op) {
            var o1 = toInteger(a);
            var o2 = toInteger(b);
            var n1 = o1.num;
            var n2 = o2.num;
            var t1 = o1.times;
            var t2 = o2.times;
            var max = t1 > t2 ? t1 : t2;
            var result = null;
            switch (op) {
                case 'add':
                    if (t1 === t2) { // 两个小数位数相同
                        result = n1 + n2
                    } else if (t1 > t2) { // o1 小数位 大于 o2
                        result = n1 + n2 * (t1 / t2)
                    } else { // o1 小数位 小于 o2
                        result = n1 * (t2 / t1) + n2
                    }
                    return result / max;
                case 'subtract':
                    if (t1 === t2) {
                        result = n1 - n2
                    } else if (t1 > t2) {
                        result = n1 - n2 * (t1 / t2)
                    } else {
                        result = n1 * (t2 / t1) - n2
                    }
                    return result / max;
                case 'multiply':
                    result = (n1 * n2) / (t1 * t2);
                    return result;
                case 'divide':
                    result = (n1 / n2) * (t2 / t1);
                    return result
            }
        }
 
        // 加减乘除的四个接口
        function add(a, b) {
            return operation(a, b, 'add')
        }
 
        function subtract(a, b) {
            return operation(a, b, 'subtract')
        }
 
        function multiply(a, b) {
            return operation(a, b, 'multiply')
        }
 
        function divide(a, b) {
            return operation(a, b, 'divide')
        }
 
        // exports
        return {
            add: add,
            subtract: subtract,
            multiply: multiply,
            divide: divide
        }
    }();

Call the method:

console.log(floatObj.add(0.1, 0.2));//0.3
console.log(floatObj.subtract(1.0, 0.9));//0.1
console.log(floatObj.multiply(19.9, 100));//1990
console.log(floatObj.divide(6.6, 0.2));//33

Original link https://blog.csdn.net/qq_33237207/article/details/82109352

Guess you like

Origin blog.csdn.net/ethan__xu/article/details/90602766