问题描述
使用 JPA 映射一个 float 类型到数据库:
然后存储 129364.57,发现存储的结果是 129365。
从上周就开始研究这个问题,查阅了各种资料,网上许多人都说是因为 MySQL 默认是保留六位有效数字,自己测试了一下也确实是这样。但是查询 MYSQL 官方文档,并没有找到依据。
MySQL 官方文档:Float – MySQL
如果你看到了这篇文章,欢迎评论发表意见,让我们互相学习、进步。
结论
写的非常好的一篇文章,MySQL 存储的各种尝试:MySQL 数字类型 int 与 tinyint、float 与 decimal 如何选择
用 JPA 映射的 Float 默认的长度与小数点都是 0。
这是测试的结果:float 默认能精确到 6 位有效数字!
原理猜想
这是 MySQL 官方文档对 float 和 double 的描述:
官方文档并没有说 MySQL 是如何存储浮点数的,所以如果没有去读过其源代码,所有的博客都只是猜想。
IEEE 754
目前大多数人认为 MySQL 内部是采用 IEEE 754 进行存储的,IEEE 754 – 维基百科。
IEEE 二进制浮点数算术标准(IEEE 754)是 20 世纪 80 年代以来最广泛使用的浮点数运算标准,为许多 CPU 与浮点运算器所采用。
以 32 位的单精度浮点数为例:
与日常所说的科学计数法类似。
因为底数是有效数字,所以第一位肯定是 1,所以这个 1 不进行存储,所以虽然是 23 位的底数,但是实际的底数位数其实是 24 位,含有一个隐含的 1。
双精度与此类似,一个符号位,指数位为 11,尾数为 52 位,合计 64 位。
假设
假设是用 MySQL 是用 IEEE 754 标准存储的浮点数。
用单精度存储 129364.57,其二进制为 11111100101010100.1001000111101011100001010001111011。
正数:符号位为 0。
指数位为:00010000(十进制中的 16)。
去掉第一个 1,保留 23 位,底数为:11111001010101001001000。
所以最后的结果是 11111100101010100.1001000,转换为十进制为:129364.5625。
但是实际的 MySQL 存储后的结果为 129365,所以猜想要么 MySQL 就是不是按 IEEE 754 存储的,要么就是按这个存储的但是内部为了数据库的性能等或其他的优化对数据进行了处理。
这里我这里更倾向于第二种,毕竟 IEEE 754 是一种国际标准,没有理由不遵守。