js浮点金额计算精度

在js中进行以元为单位进行浮点数计算时,会产生精度问题,例如:

console.log(0.1+0.2)
结果为:0.30000000000000004

大多数编程语言计算采用的是IEEE 754 标准,那么先来看下浮点数运算产生误差的原因,拿0.1+0.2=0.30000000000000004举例。
首先,站在计算机的角度思考 0.1 + 0.2 这个问题。我们知道,能被计算机读懂的是二进制,而不是十进制,所以我们先把 0.1 和 0.2 转换成二进制看看:

0.1 => 0.0001 1001 1001 1001…(无限循环)
0.2 => 0.0011 0011 0011 0011…(无限循环)

0.1和0.2转化为二进制之后,变成了一个无限循环的数字,这在现实生活中,无限循环我们可以理解,但计算机是不允许无限循环的,对于无限循环的小数,计算机会进行舍入处理。进行双精度浮点数的小数部分最多支持 52 位,所以两者相加之后得到这么一串 0.0100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 ,因浮点数小数位的限制而截断的二进制数字,这时候,我们再把它转换为十进制,就成了 0.30000000000000004。

解决办法:

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
        }
    }();

调用方法:

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

如果要求金额保留两位小数的话,最开始想法是使用toFixed(2),可是在谷歌浏览器下如果遇到例如1.535.toFixed(2)的话值为1.53,并不能进行四舍五入(很奇怪的是1.635.toFixed(2)可以,隔个单位就行,估计是和二进制存储机制有关),所以这种方法肯定不精确。

查到到的一个办法是先把浮点数转成整数,然后使用Math.round四舍五入,例如:

Math.round(totalPrice * 100) / 100
Math.round(1.535 * 100) / 100   // 成功转成1.54

文章参考:https://blog.csdn.net/qq_33237207/article/details/82109352

原文地址:https://www.cnblogs.com/dxzg/p/10548823.html

时间: 2024-11-11 11:00:50

js浮点金额计算精度的相关文章

js 浮点小数计算精度问题 parseFloat 精度问题

在js中进行以元为单位进行金额计算时 使用parseFloat会产生精度问题var price = 10.99;var quantity = 7;var needPay = parseFloat(price * quantity); needPay的正确结果应该是76.93元  但是运行后发现needPay为76.93000000000001 此情况可通过 toFixed(n)  方法修正 但是这个方法对 js版本要求较高 不能兼容ie5 另一个解决方案是: 将元为单位的金额乘以100换算为分进

js float浮点数计算精度问题

/** * floatObj 包含加减乘除四个方法,能确保浮点数运算不丢失精度 * * 我们知道计算机编程语言里浮点数计算会存在精度丢失问题(或称舍入误差),其根本原因是二进制和实现位数限制有些数无法有限表示 * 以下是十进制小数对应的二进制表示 * 0.1 >> 0.0001 1001 1001 1001…(1001无限循环) * 0.2 >> 0.0011 0011 0011 0011…(0011无限循环) * 计算机里每种数据类型的存储是一个有限宽度,比如 JavaScrip

js中进行金额计算parseFloat

在js中进行以元为单位进行金额计算时 使用parseFloat会产生精度问题var price = 10.99;var quantity = 7;var needPay = parseFloat(price * quantity); needPay的正确结果应该是76.93元  但是运行后发现needPay为76.93000000000001 此情况可通过 toFixed(n)  方法修正 但是这个方法对 js版本要求较高 不能兼容ie5 另一个解决方案是: 将元为单位的金额乘以100换算为分进

JS数字计算精度误差的解决方法

本篇文章主要是对javascript避免数字计算精度误差的方法进行了介绍,需要的朋友可以过来参考下,希望对大家有所帮助. 如果我问你 0.1 + 0.2 等于几?你可能会送我一个白眼,0.1 + 0.2 = 0.3 啊,那还用问吗?连幼儿园的小朋友都会回答这么小儿科的问题了.但是你知道吗,同样的问题放在编程语言中,或许就不是想象中那么简单的事儿了. 不信?我们可以做个试验. 先来看一段 JS. var num1 = 0.1; var num2 = 0.2; alert(num1+num2 ===

js限制金额输入框(最高精度为保留6位小数)

js限制金额输入框(最高精度为 保留6位小数) 说明:在一个项目中有限制6位小数的限制.在本次测试的过程中发现还存在一些bug 1.如可以长按键盘不放可输入多个小数点 2.通过长按可输入'1...234.' 这次在解决了这个bug过程中也稍带学习了一下正则表达式,以前觉得正则表达式只是用来做字段的教研,但是现在发现用来匹配字符,并且结合各个语言提供的api能做不少事儿,这里把代码贴了出来供大家参考. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 T

关于JS数学计算精度不准和自动转科学计数法的问题

在javaScript中,数学计算是一个很坑爹的问题, 由于系统二进制转十进制的误差,导致浮点数的运算精度很不理想,经常会出现一些意想不到的问题. 而js自动将小数转为科学计数法的问题,也让一些自己写数学插件的同学痛苦不已. 第一个,浮点数计算精度: 比如:1.2*3 = 3.599999999.... 解决办法: ⑴将浮点数乘以10的n次幂化为整数,再进行计算,之后再除以10的n次幂,就可以得到我们想要的数值.即:1.2*3 = 12*3/10 = 3.6;(n为小数点后数位之和); 至于多个

JS数字计算精度问题解决

1 add(a, b) {//相加 2 var c, d, e; 3 try { 4 c = a.toString().split(".")[1].length; 5 } catch (f) { 6 c = 0; 7 } 8 try { 9 d = b.toString().split(".")[1].length; 10 } catch (f) { 11 d = 0; 12 } 13 return e = Math.pow(10, Math.max(c, d)),

关于js中计算精度的问题解决办法

/** * 解决js中浮点数的精度问题 * @parem f 要精度的值 * @parem digit 要精确的位数 * */ var htmlObj = {};htmlObj.formatFloat = function(f, digit) { var m = Math.pow(10, digit);//Math.pow(x,y)返回 x 的 y 次幂的值 return parseInt(f * m, 10) / m;};

JAVA中高精度金额计算

一般java代码中遇到高精度金额计算,日常使用bigDecimal类型. 在使用BigDecimal类来进行计算的时候,主要分为以下步骤: 1.用float或者double变量构建BigDecimal对象. 2.通过调用BigDecimal的加,减,乘,除等相应的方法进行算术运算. 3.把BigDecimal对象转换成float,double,int等类型. 一般来说,可以使用BigDecimal的构造方法或者静态方法的valueOf()方法把基本类型的变量构建成BigDecimal对象. 1