一篇文章带你深入探讨 DECIMAL 解决 MySQL 数据精度丢失问题
Posted 南淮北安
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一篇文章带你深入探讨 DECIMAL 解决 MySQL 数据精度丢失问题相关的知识,希望对你有一定的参考价值。
一、数据精度丢失是如何表现的?
不要盲目的说float和double精度可能发生丢失,而是说在存取时因为精度不一致会发生丢失,当然这里的丢失指的是扩展或者截断了,丢失了原有的精度。
decimal是好,但不是说不会发生任何精度丢失。如果问题看得不深入,总会以偏概全。
我们知道,mysql存储小数可以使用float、double、decimal等。
这些类型存储的小数精度都比较高。我们平时应用最多的就是两位小数点,所以,这些类型都是可以满足的。
当我们不知道原因的时候,总会认为到处都是问题,找不到关键点。在应用的时候,总是不踏实,唯唯诺诺的。搞清楚缘由,找到问题的关键点,然后加以深入理解,方可给自己一个定心丸。
精度变化不管是哪种类型,都会发生。网上言传float、double精度可能丢失,decimal精度不会丢失,所以建议decimal来存储金额值。
但是,没有指明确切缘由,只是大概是这么回事,其实离真相还差得远。
在mysql中,我们用【小数数据类型(总长度,小数点长度)】来表示小数的总长度和小数点后面的长度,如decimal(m,n)
。n就是小数点后面的 数字个数。float(m,n)、double(m,n)
含义差不多,都是定义长度和精度的。
既然都定义了精度,为什么还会发生所谓的精度丢失问题呢?
二、精度丢失是什么原因?
查阅网上的说法,要么是没有设置精度位数,要么就是设置的精度和存储时的精度不一致,要么就是mysql存的,用其他数据库引擎查询。基本是这三类可以概括各种情况。
这三种有什么共性和差异性呢?
如果能够将这三种情况抽象为一个共同的模型,我们就找到了精度丢失问题的关键。而不是盲人摸象般的局部就事论事,以偏概全了。
第一种情况:
没有设置精度就是使用默认的精度,此时的策略就是,尽可能保证精度,因此一般使用最高精度存储数据的。如果设置数据类型指定了精度,那么存储数据时就按照设置的精度来存储。
假设默认精度为6个小数位或设置为6位,我们存储小数一般是:2,1.1,2.11,32.214,41.4513,5.21452,6.214522,7.1421457,这里有8个不同的小数位的数,在存储到数据库时会发生什么问题?
使用默认精度,数据库用6位来存储。
所以前面6个数字,精度都不够6位。
当然,存储时如果是float和double,那么会尽可能以近似的值存储,以保证精度。所以,结果可能是:2.00000,1.10000, 2.11000,32.213999,41.451301,5.214519。
如果用的是decimal,则结果是:2 1.1 2.11 32.214 41.4513 5.21452 。
从这个结果可以看到,decimal的存储结果没有精度丢失问题。因为decimal内部以字符形式存储小数,属于准确存储。
而float和 double等则属于浮点数数字存储,所以没有办法做到准确,只能尽可能近似。这也是大家选用decimal的原因,也认为decimal精度不会丢失的 原因。
这个结果对,但不全对。数据的精度丢失发生在存储和获取这一瞬间。所以,存储和获取这两个方面也就是精度发生变化的时刻。这里的精度丢失发生在存储时。这里举例使用6位精度来兼容不同的精度数值。还有6位精度和大于6位精度的值我们还没有讨论,现在来讨论一下。
是不是6位精度值存入6位精度类型精度就不会丢失了呢?必须不能丢失。如果丢失了,谁还敢用这样的类型呢?这样的类型还有什么意义呢?所以,6.214522存入6位小数的float和double是不会丢失小数精度的,取出来的数还是6.214522。
也就是说,一个小数存入相同的精度的数据类型时,精度是不会丢失的。
第二种情况:
当7或更多位精度的数字存入6位精度类型字段时,会发生什么?
结果会发生四舍五入。四舍五入的结果就是匹配字段的数据类型的精度长度。此时精 度也会丢失。不管内部如何处理,我们得到的数据是经过四舍五入的。
但是有一点可以确定,我们在读取取舍后的数字时,是固定的。虽然浮点数存储的不是确切的 数值,但是在你指定的精度长度条件下,存取都是确定的一个数值。而发生精度变化的就是数值的精度和字段的精度长度不匹配,从而发生数值扩展精度和截断精度 问题,这也就是浮点数精度不准确的问题。
有人说,用浮点数存储在查询时会发生无法匹配的问题。例如,存入的数据为15.21,查询的时候用15.21竟然匹配不到15.21这条记录。
然而这是个 人没有理解数据库浮点数处理问题。我反正是可以准确匹配查询的。在设置两位精度的字段数据类型时,存取数据得到的都是两位精度的值,在查询时用两位的数值 自然也是可以匹配到的
匹配不到的情况是发生在精度不对等的情况,就是前面讨论的扩展了精度和截断了精度的情况。这本身就是数据库使用不正确的问题,并不 能说明float和double不能用在金额上的存储。
然而网上并没有人对此深入思考过。不是转载就是重复同一个观点,缺乏思考。
第三种情况:
mysql数据库使用其他数据库引擎来查询,得到的结果会发生精度丢失。
这个精度丢失的原因,就可能是不同的数据库引擎对浮点数的精度扩展 和截断处理策略不一致,而且,存储时策略也不一致。
所以导致精度会出现各种变化。这种问题也就是催生decimal类型的出现。
我们前面看到的 decimal是可以确切存储小数的精度的。因为在存储的时候会将小数以字符串存储,就不会再发生精度的扩展问题。
但是decimal依然会发生精度截断 问题。如果decimal指定精度为2位小数,存入的是这样的值:12.123,你觉得结果如何?
当然还是会发生四舍五入。
结果就是12.12,然而 12.12以字符串形式存入了数据库。
此后,12.12始终都是12.12,表现出来的是小数,然而内部是字符串形式存储,所以,小数精度不会再发生变化 了。
我们不管以什么精度来获取这个值,都是12.12,而且,不管是一般数据库引擎读取到的也都是12.12,所以decimal才是大家推荐使用的金额 存储类型。
我们应该知道,数据库作为中心,驱动着各种软件的发展,然而在实现上,我们可能用Java方式来访问MySQL,也可能是.Net方式访问mysql,不 同的语言的mysql引擎是有差别的。
但是不管怎么样,decimal类型都是可以确定精度的,不会发生四舍五入。所以在各个软件中都表现良好
所以,不要盲目的说float和double精度可能发生丢失,而是说在存取时因为精度不一致会发生丢失,当然这里的丢失指的是扩展或者截断了,丢失了原 有的精度。
decimal是好,但不是说不会发生任何精度丢失。如果问题看得不深入,总会以偏概全。
decimal占用的字节多。不过在计算的时候,数据 是准确的。
而float和double在计算前一定要对取到的数据的精度做处理,确保和存储的精度一致。而存储的精度也和需要的精度一致,不要长也不要 短。
以上是关于一篇文章带你深入探讨 DECIMAL 解决 MySQL 数据精度丢失问题的主要内容,如果未能解决你的问题,请参考以下文章