浮点数的这些坑,你未必知道
我猜作为开发工程师,大部分人都用过浮点数。但是你是否用对了呢?你是否知道,浮点数有近一半的值,在-1与+1以内呢? 本节大纲有: 1、基本数据类型 2、认识浮点数规律 3、我在浮点数上踩过的坑 在计算机的眼中,一切都是数字,一切都是二进制。 一、基本数据类型 关于数值,你应该时刻牢记在心里的三点, 1、每个基本类型的数值都是有范围限制的,不是无限大的。 2、无论是boolean,int,float,string,struct,object,最终都对应计算机的一个byte或者多个byte。 3、非必需,不要使用浮点数。 下面说几个在工作中会经常遇到的几个问题: 1、计算机中32位的有符号整型int,其最大值就是2147483647,这个数大约是21亿多。如果放普通计算应该都不成问题。但是,如果你数据库表的主键是int类型,那你就要小心了,好多大厂都在这个数据类型上面栽过跟头。因为现在中国网民基数太大,一张表很容易上亿,一旦超过21亿多,你的系统可能会因为db主键溢出导致故障。 2、在金融行业应该注意数值溢出的问题。两个integer相加或者相乘,其结果值很有可能超过integer的表示范围。在金融这种对精确度要求特别高的行业,这种错误更是不可忍受的。 3、返回给前端的json数据,尽量不要使用数值类型。如果你做过前端相关的项目,你会发现,当你使用json格式字符串给前端返回数值类型时,前端展示出来的数字可能跟你返回的数字完全不一样。其根本原因就是Javascript的浮点数没有办法精确表示较大数值。 二、认识浮点数规律 如果你没有耐心去深入理解浮点数(说句实话,确实复杂),那么我建议你至少应该记住关于浮点数的几个规律和建议。
- Javascript中的数字都是双精度浮点数。
- 在浮点数中,0有+0和-0两种表示方法
- 浮点数表示的值是不连续的,不均匀的。越大的数,越无法用浮点数表示出来。
- 有近一半的浮点数数值分布在-1和+1之间。
- 不要用浮点数来生成随机数。
- 不要用浮点数来存储或计算有关金钱和资产方面的一切,包括常见的会员积分。不能用浮点数用于任何需要精确值的运算,因为浮点数无法精确的表示数字,比如说0.1就无法用浮点数精确表示出来。
文章图片
图一
上图列出的是正浮点数,即只列出了0及以上127个数值部分。 其中第二列为二进制表示。从上至下是连续增长的二进制,一共有128-8=120个状态值(01111000之后有8个状态值浮点数没有使用),但是因为是浮点数,所以其表示的值,需要经过一定计算规则才能得出,最后一列就是二进制对应十进制值。 8位浮点数,其所表示的数值范围是0-240。尽管其二进制只有120个状态值,但是其表示的最大值却达到了240,所以在240以内,一定有很多整数值,无法用8位浮点数表示。所以说,浮点数表示的值,不是连续的。如果将其能够表现出来的值,使用蓝点画出来,其所表示的数值大概如下:
文章图片
文章图片
图二
图出自Computer Systems: A Programmer's Perspective。 从上图可以看出,浮点数表示的数值,靠近0中间最多,越靠外(数值越大),浮点数数值越少。比如从图一的表中可以看出,数值224-240之间的整数,都没有办法用8位的浮点数表示出来。所以说,越大的数,其用浮点数表示的概率越低。 另外,如果你利用浮点数小数乘以一个倍数来生成整数随机数,你会发现,你生成的随机数出现跟浮点数一样的规律,即生成的随机数大多数都集中靠近中心0的位置。所以,不要利用浮点数生成随机数。 我们放大-1至+1范围的数值
文章图片
图三
我们发现在1以内,像0.1,0.2等小数,也是没有办法用8位浮点数表示出来的。从这里也可以看出,浮点数的0有+0和-0之分。 我们使用8位浮点数和32位浮点,计算一下1以内和1以外的规格化数字的大概个数。因为64位太大,所以我这里就不再计算了,但是其数值个数比例应该是类似的。
浮点数 | 8位浮点数 | 32位浮点数 |
1的二进制 | 0 0111 000 | 0 0111 1111 000 0000 0000 0000 0000 0000 |
1以内数值个数 | 56 | 1,065,353,216 |
最大规格数二进制 | 0 1110 111 | 0 1111 1110 111 1111 1111 1111 1111 1111 |
最大规格数表示的数值 | 119 | |
1以上二进制个数 | 119-56= 63 | 2,139,095,039 -1,065,353,216= 1,073,741,823 |
public class FloatTest { public static void main(String[] args) { double a = 1.0/10; double b = 1-0.9; System.out.println("1.0/10="+a); System.out.println("1-0.9="+b); System.out.println(a==b); } }
在我的电脑上输出如下
1.0/10=0.1 1-0.9=0.09999999999999998 false
相关阅读:
二进制 做支付遇到的httpclient大坑 参考资料: 浮点数表示_shuzfan的专栏-CSDN博客_浮点数 浮点数不能表示的最小正整数是? - 知乎 (zhihu.com) 浅谈JavaScript浮点数及其运算 - theWalker - 博客园 (cnblogs.com) 【浮点数的这些坑,你未必知道】
推荐阅读
- 热闹中的孤独
- JAVA(抽象类与接口的区别&重载与重写&内存泄漏)
- 放屁有这三个特征的,请注意啦!这说明你的身体毒素太多
- 一个人的旅行,三亚
- 布丽吉特,人生绝对的赢家
- 慢慢的美丽
- 尽力
- 一个小故事,我的思考。
- 家乡的那条小河
- 《真与假的困惑》???|《真与假的困惑》??? ——致良知是一种伟大的力量