Java 程序员在涉及金额的计算时,一般都会使用 BigDecimal 类型,无它,使用浮点数(float、double)太不好驾驭了,很容易出现各种想到想不到的问题!
举个例子,假如我们有一张表,用来存储用户的积分,表定义如下:
1 2 3 4 5 |
CREATE TABLE `f` ( `f1` FLOAT(10,2) DEFAULT NULL ) ENGINE=INNODB DEFAULT CHARSET=utf-8 |
然后向这个表里插入 131072.32 的积分值,如下所示
1 |
INSERT INTO f VALUE (131072.32); |
再进行查询(我的PC上亲自测试的):
SELECT * FROM f;
结果是多少呢?
你会发现不是 131072.32 ,而是 131072.31 ,what the Fuck !?怎么回事?

我手动将数据库中这个 131072.31 改成 131072.32,然后保存,刷新一下,发现这个值还是 131072.31 !

那么,我不用 sql 直接往数据库里插入,而是用代码往数据库插入一条值是 131072.31 的数据呢?

发现数据库里存的还是 131072.31 !!
为什么呢?
我就直接说吧,其实跟这个字段的类型有关,这个字段是这样一种格式:
1 2 3 4 5 |
CREATE TABLE `f` ( `f1` FLOAT(10,2) DEFAULT NULL ) ENGINE=INNODB DEFAULT CHARSET=utf-8 |
如上,看到没有,FLOAT(10,2) 是什么意思呢?
float(M, D) 中 M 和 D 的含义?
FLOAT(10,2) 表示一共能存 M 位,其中小数点后占 D 位。
注意,是小数点后面占 D 位,不是精确到小数点后面 D 位!也不是只存储小数点后面 D 位!
如果你将这个字段改成 FLOAT(10,3) :

你再次查询,会发现数据库中 131072.31 这个数变成了:

如果你将这个字段改成 FLOAT(10,4) :

也就是说,float(M, D) 中的 D 控制的事小数点后面“显示”几位,有可能实际存的值小数点后面好多位,但是只显示 D 位!
131072.32 中的小数部分 0.32 没法精确用二进制小数表示:
0.32D 约等于 0.0101B ,而 0.0101B 再转成十进制小数是 0.3125D ,这就导致误差了!
另外:
1 2 |
0.32D 约等于 0.0101000111101011100001010001111010111000010100011111.... 0.0101000111101011100001010001111010111000010100011111 转换成十进制小数是 0.32000000000000006 |
float(M, D) 中 M 的设置,也会影响数据展示和准确性!小数点前面的数字超过 M 的位数限制也可能会出现误差!
举例
浮点数相加,随着次数越来越多,误差会越来越大!
1 2 3 4 5 6 7 8 |
public static void test2误差累计越来越多() { float a1 = 0f; for (int i = 0; i < 1000000; i++) { a1 = a1 + 0.3f; } // 精确的结果应该是 300000 System.out.println(a1);// 299546.7 System.out.printf("%.20f%n", a1);// 299546.68750000000000000000 } |