js浮点数计算-阿里云开发者社区

开发者社区> 技术小牛人> 正文

js浮点数计算

简介:
+关注继续查看

由于二进制精度影响,JS在计算浮点数时容易造成偏差,而这个问题在很多程序语言中都存在,所以部分程序语言专门提供了高精度函数,为的就是解决浮点数据计算。


JS 中貌似还没有出现这类函数库,因此在浮点数计算中,很容易出现精度问题,当然JS是弱类型语言,很多时候程序自己转类型因此不注意容易忽视,比如:

1
alert(0.57*100)

wKioL1aZp_OxZBcrAAAHNU-G_TY923.png


这种精度问题只会在浮点数中出现,也就是说只要把浮点数提取为成整数计算就可以避免这个问题:


  1. 当拆分为整数时,注意借位进位,正负数处理,相对于说会减小计算的数量大小,能更好的精确,但处理过程复杂。

  2. 转成对等整数计算,不处理进位,正负数,只需要处理好小数点位数,计算数量偏小,处理方便,大数据量不宜使用。


下面为第二种方法解决浮点数计算的代码:(程序未进行严格测试,如有问题欢迎留言!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
(function (win) {
    var parameToString = function (parame) {//转字符串
        return typeof parame === 'string' ? parame : parame.toString();
    }, parameSplit = function (parame) {//拆分浮点数
        var split = parameToString(parame).split('.', 2), integer, decimal = '';
        integer = parameToNumber(split[0]);
        if (split.length === 2) {
            decimal = split[1];
        }
        return [integer, decimal];
    }, parameToNumber = function (parame) {//转数值
        parame = parseInt(parame);
        return typeof parame === 'number' ? parame : 0;
    }, parameCompleteAfterNumber = function (parame, length) {//数字后补齐
        if (length > 0) {
            return parame + Math.pow(10, length - parame.length).toString().substr(1);
        }
        return '';
    }, parameCompleteBeforeNumber = function (parame, length) {//数字前补齐
        if (parame.length < length) {
            var insufficient = length - parame.length;
            return parameToString(insufficient * 10).substr(1) + parame;
        else if (parame.length === length) {
            return parame;
        }
        return false;
    }, resultSplit = function (parame, decimalLength) {//结果小数拆分
        if (decimalLength === 0) {
            return integer;
        }
        var parames = parameSplit(parame), integer = parameToString(parames[0]), result;
        if (integer.length > decimalLength) {
            var diff = integer.length - decimalLength;
            result = integer.substr(0, diff) + '.' + integer.substr(diff);
        else if (integer.length === decimalLength) {
            result = '0.' + integer;
        else {
            result = '0.' + parameCompleteBeforeNumber(integer, decimalLength);
        }
        return Number(result + parames[1]);
    }, parameCompleteParse = function (parame1, parame2) {//参数补全解析处理
        parame1 = parameSplit(parame1);
        parame2 = parameSplit(parame2);
        var length = Math.max(parame1[1].length, parame2[1].length);
        return [parameToNumber(parame1[0] + parameCompleteAfterNumber(parame1[1], length)), parameToNumber(parame2[0] + parameCompleteAfterNumber(parame2[1], length)), length];
    }, math = {
        //加法计算
        bcadd: function (parame1, parame2) {
            var parames = parameCompleteParse(parame1, parame2), result = parames[0] + parames[1];
            return resultSplit(result, parames[2]);
        },
        //比较两个数字
        bccomp: function (parame1, parame2) {
            var parames = parameCompleteParse(parame1, parame2);
            if (parames[0] > parames[1]) {
                return 1;
            else if (parames[0] === parames[1]) {
                return 0;
            else {
                return -1;
            }
        },
        //除法计算
        bcdiv: function (parame1, parame2) {
            var parames = parameCompleteParse(parame1, parame2);
            return parames[0] / parames[1];
        },
        //取模
        bcmod: function (parame, modulus) {
            var parames = parameCompleteParse(parame, modulus), result = parames[0] % parames[1];
            if (parames[2] > 0) {
                return resultSplit(result, parames[2]);
            }
            return result;
        },
        //乘法计算
        bcmul: function (parame1, parame2) {
            var parames = parameCompleteParse(parame1, parame2), result = parames[0] * parames[1];
            if (parames[2] > 0) {
                return resultSplit(result, parames[2] * 2);
            }
            return result;
        },
        //二次方根
        bcsqrt: function (parame) {
            var split = parameSplit(parame), length = split[1].length, result;
            if (length > 0) {
                length += length % 2;
                parame = Number(split[0] + parameCompleteAfterNumber(split[1], length));
            }
            result = Math.sqrt(parame);
            if (length > 0) {//小数点向前移动
                return resultSplit(result, length / 2);
            }
            return result;
        },
        //减法
        bcsub: function (parame1, parame2) {
            var parames = parameCompleteParse(parame1, parame2), result = parames[0] - parames[1];
            return resultSplit(result, parames[2]);
        }
    };
    for (var key in math) {
        win[key] = math[key];
    }
})(window);
 
//使用
alert(bcmul(0.57, 101.1));
alert(bcmod(2.1, 0.5));
alert(bcsqrt(0.024));



是什么原因造成的?最主要原因是计算机只识别二进制数,所有数据都会转成二进制数运算得来,并不是程序不会计算小数形式的二进制数,而是在程序中进制数转换造成的。


十进制转二进制:

整数部分(除2取余,逆序排列):

    即不断整除2,取余数(0或1)为二进制数,商再除2如此循环(直到商为0),把得出来的余数(二进制数)以先后计算倒序取出余数,即为对应二进制数。如:

    10 转二进制结果是:

    10/2 = 5  余  0

     5/2 = 2 余  1

     2/2  =1 余  0

     1/2  =0  余  1

以先后计算倒序取出余数则结果为: 1010


小数部分(乘2取整,顺序排列):

    即不断乘2取整数部分(0或1),取结果中小数部分再乘2如此循环(直到结果无小数部分,当然大部分无法计算到无小数部分,所以就会出现截断,最终影响计算精度),把得出来的整数(二进制数)以先后计算顺序取出,即为对应小数二进制数。如:

   0.35 转二进制结果是:

    0.35*2  = 0.7  整0小0.7

    0.7*2  = 1.4  整1小0.4

    0.4*2  =  0.8  整0小0.8    ---无限循环开始

    0.8*2  = 1.6   整1小0.6

    0.6*2  = 1.2  整1小0.2

    0.2*2  = 0.4  整0小0.4     --- 循环结束

    0.4*2  = 0.8  整0小0.8

    0.8*2  = 1.6  整1小0.6

    0.6*2  = 1.2  整1小0.2

    0.2*2  = 0.4  整0小0.4

    0.4*2  = 0.8  整0小0.8

    .

    .

    .

以先后计算顺序取出整数则结果为:0.01011001100.....   截取长度取决于程序支持的长度。



二进制转十进制:

按权相加2n,个位为n=0,小数部分n-1,整数(个位以上)部分n+1,乘以对应位二进制数。如:

1101.101 转十进制结果是:

23*1 + 22*1 +21*0 + 20*1 + 2-1*1 + 2-2*0  + 2-3*1 = 8 + 4 + 0 + 1 + 0.5 + 0 + 0.125 = 13.625



注:浮点数从转换中可以看出来二进制数转十进制数不存在精确度丢失,而十进制数转二进制数很容易造成精确度丢失,从而影响计算结果精度。

本文转自  ttlxihuan    51CTO博客,原文链接:http://blog.51cto.com/php2012web/1735982

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
阿里云服务器怎么设置密码?怎么停机?怎么重启服务器?
如果在创建实例时没有设置密码,或者密码丢失,您可以在控制台上重新设置实例的登录密码。本文仅描述如何在 ECS 管理控制台上修改实例登录密码。
9497 0
使用OpenApi弹性释放和设置云服务器ECS释放
云服务器ECS的一个重要特性就是按需创建资源。您可以在业务高峰期按需弹性的自定义规则进行资源创建,在完成业务计算的时候释放资源。本篇将提供几个Tips帮助您更加容易和自动化的完成云服务器的释放和弹性设置。
12037 0
阿里云服务器如何登录?阿里云服务器的三种登录方法
购买阿里云ECS云服务器后如何登录?场景不同,阿里云优惠总结大概有三种登录方式: 登录到ECS云服务器控制台 在ECS云服务器控制台用户可以更改密码、更换系.
13186 0
windows server 2008阿里云ECS服务器安全设置
最近我们Sinesafe安全公司在为客户使用阿里云ecs服务器做安全的过程中,发现服务器基础安全性都没有做。为了为站长们提供更加有效的安全基础解决方案,我们Sinesafe将对阿里云服务器win2008 系统进行基础安全部署实战过程! 比较重要的几部分 1.
9054 0
腾讯云服务器 设置ngxin + fastdfs +tomcat 开机自启动
在tomcat中新建一个可以启动的 .sh 脚本文件 /usr/local/tomcat7/bin/ export JAVA_HOME=/usr/local/java/jdk7 export PATH=$JAVA_HOME/bin/:$PATH export CLASSPATH=.
4621 0
阿里云服务器ECS登录用户名是什么?系统不同默认账号也不同
阿里云服务器Windows系统默认用户名administrator,Linux镜像服务器用户名root
4012 0
5723
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载