123457000
+ 1 = 123456793?这是什么bug?MySQL的这个“bug”来源于网友的问题,如图:有经验的同学第一反应就是这个value字段显然不是整型(MySQL不会犯这么简单的错误),show create table 可以看到,value字段是一个float类型。而且实际上,这一行是用 insert into demo
values(1,123456789); 这个命令插入的。从这个表格可以看到,float字段占用空间只有4个字节,但是可以表示的数据范围达到10的38次方那么大,这显然不科学。我们都知道4个字节能够表示的不同的数据,就只有2的32次方,即使只表示正数都不可能达到这么大,何况float字段还要能存储小数部分。细心的同学会追问,实际存储的是哪个值呢,为什么select出来value和value+1的结果本身也有矛盾。这就要说到显示问题,由于float理论上长度无限,因此转成字符串类型显示的时候,需要考虑到整数和小数部分分别用用多少字节来显示。Value字段是float类型,就按照float to string的规则,Value+1计算后的类型是double,就按照double to string的规则。为了验证这个结论,我们可以修改value字段的显示定义,alter
table demo change value value float(100,2);
所以实际原因就是,由于float字段存储采用了近似值,因此插入123456789这个值的时候,实际存储的是123456792。实际上double字段也有类似的问题,只是因为double有8个字节,要用更大的数字来复现这个“bug”。对于金融等需要精确存储和计算的系统,我们都建议使用decimal字段。以上的结论基本就够指导我们平时的使用了。可能有同学还是想再追问一下,float这个用近似值存储的原因是知道了,那这个近似值是怎么算的呢?MySQL的浮点数类型(FLOAT和DOUBLE)是用二进制表示的,所以它们不能精确地存储所有的十进制数。当整数部分比较大时,MySQL会用近似值来存储浮点数,这个近似值的取值规则是基于IEEE 754标准的。IEEE
754标准定义了浮点数的格式和运算,它将一个浮点数分为三个部分:符号位(S)、指数位(E)和尾数位(M)。符号位表示正负号,指数位表示幂次,尾数位表示有效数字。根据这三个部分,一个浮点数可以表示为:其中,M的值是根据尾数位的二进制值和浮点数类型的不同而确定的。对于FLOAT类型,尾数位有23位,所以M的值是:其中,bi是第i位的二进制值。对于DOUBLE类型,尾数位有52位,所以M的值是:E的值是根据指数位的二进制值和浮点数类型的不同而确定的。对于FLOAT类型,指数位有8位,所以E的值是:根据这些公式,我们可以计算出一个浮点数在MySQL中存储时的近似值。例如,假设我们要存储一个十进制数123456789,我们可以先将它转换为二进制形式:接下来,我们可以根据浮点数类型的不同来确定S、E和M的值。如果我们选择FLOAT类型,那么S、E和M的值分别为:注意,由于FLOAT类型只有23位尾数位,所以我们只能保留前23位有效数字,后面的数字被舍弃了。这就导致了精度损失。如果我们将这些值转换回十进制形式,我们得到的近似值就是:https://doc.kunlunbase.com/zh/Klustron_Instruction_Manual.htmlhttps://doc.kunlunbase.com/zh/Klustron_Quickly_Guide.htmlhttps://doc.kunlunbase.com/zh/Klustron-function-experience-example.htmlhttps://doc.kunlunbase.com/zh/product-usage-and-evaluation-guidelines.html同时欢迎大家扫码👇添加小助手(备注:加入Klustron技术交流群)欢迎大家在交流群共同探讨更多问题及主题。