decimal,float和double的区别
onekbit@163.com 2019-03-04 17:17:17 990人已围观
经常在开发过程中看见decimal,float和double这3种数据类型,都可以用来作为浮点数值的存储,今天就研究一下这3种类型之间的区别:
float:
float数值类型用于表示单精度浮点数值,含字节数为4,32bit,数值范围为-3.4E38~3.4E38(7个有效位)是近似存储,并不精确
double:
double数值类型用于表示双精度浮点数值,含字节数为8,64bit数值范围-1.7E308~1.7E308(15个有效位)是近似存储,并不精确
decimal:
定点型,128bit,不存在精度损失,常用于银行帐目计算。(28个有效位)精确存储,凡是跟钱相关的都建议使用 Decimal。
我这里引用找到的mysql5.2中文手册中的一段描述:
FLOAT[(M,D)] [UNSIGNED] [ZEROFILL]
小(单精度)浮点数。允许的值是-3.402823466E+38到-1.175494351E-38、0和1.175494351E-38到3.402823466E+38。这些是理论限制,基于IEEE标准。实际的范围根据硬件或操作系统的不同可能稍微小些。
M是小数纵位数,D是小数点后面的位数。如果M和D被省略,根据硬件允许的限制来保存值。单精度浮点数精确到大约7位小数位。
如果指定UNSIGNED,不允许负值。
使用浮点数可能会遇到意想不到的问题,因为在MySQL中的所有计算用双精度完成。参见A.5.7节,“解决与不匹配行有关的问题”。
· DOUBLE[(M,D)] [UNSIGNED] [ZEROFILL]
普通大小(双精度)浮点数。允许的值是-1.7976931348623157E+308到-2.2250738585072014E-308、0和2.2250738585072014E-308到 1.7976931348623157E+308。这些是理论限制,基于IEEE标准。实际的范围根据硬件或操作系统的不同可能稍微小些。
M是小数总位数,D是小数点后面的位数。如果M和D被省略,根据硬件允许的限制来保存值。双精度浮点数精确到大约15位小数位。
如果指定UNSIGNED,不允许负值。
· DOUBLE PRECISION[(M,D)] [UNSIGNED] [ZEROFILL], REAL[(M,D)] [UNSIGNED] [ZEROFILL]
为DOUBLE的同义词。除了:如果SQL服务器模式包括REAL_AS_FLOAT选项,REAL是FLOAT的同义词而不是DOUBLE的同义词。
· FLOAT(p) [UNSIGNED] [ZEROFILL]
浮点数。p表示精度(以位数表示),但MySQL只使用该值来确定是否结果列的数据类型为FLOAT或DOUBLE。如果p为从0到24,数据类型变为没有M或D值的FLOAT。如果p为从25到53,数据类型变为没有M或D值的DOUBLE。结果列范围与本节前面描述的单精度FLOAT或双精度DOUBLE数据类型相同。
FLOAT(p)语法与ODBC兼容。
· DECIMAL[(M[,D])] [UNSIGNED] [ZEROFILL]
压缩的“严格”定点数。M是小数位数(精度)的总数,D是小数点(标度)后面的位数。小数点和(负数)的‘-’符号不包括在M中。如果D是0,则值没有小数点或分数部分。DECIMAL整数最大位数(M)为65。支持的十进制数的最大位数(D)是30。如果D被省略, 默认是0。如果M被省略, 默认是10。
如果指定UNSIGNED,不允许负值。
所有DECIMAL列的基本计算(+,-,*,/)用65位精度完成。
· DEC[(M[,D])] [UNSIGNED] [ZEROFILL], NUMERIC[(M[,D])] [UNSIGNED] [ZEROFILL], FIXED[(M[,D])] [UNSIGNED] [ZEROFILL]
是DECIMAL的同义词。FIXED同义词适用于与其它服务器的兼容性。
上面已经从基础层面介绍了3种数据类型,那么我还有以下问题需要研究明确:
- 第一、 如何理解单精度和双精度?
这两者的区别如果理解为“单精度是精确到小数点后一位,而双精度是精确到小数点后两位”,那就大错特错了。实际上由于float的有效位数是7位,double的有效位数是16位,因此单精度、双精度其实是指代这里的有效位数。如下图:
另外需要注意的是有效位数并不等于精确位数,纵然float可以表示到小数点后7位,但只有前6位是精确的,第7位很可能造成数据误差。而对于double来说只有前15位是精确的,第16位也很可能造成数据误差。- 第二、精度损失如何产生?
关于float、double精度丢失的问题,实际上就是被扩展(类型转换)或截断(四舍五入)了,究其缘由是因为存取时标度不一致所导致的。在录入数据时若数据的标度与定义列数据类型时设置的标度不一致,则会导致存入时以近似的值来存储,这就造成了我们上面说到的精度丢失。
那在什么情况下float、double的精度不会丢失呢?其实根据上面出问题的情况,我们可以想到当数据标度与类型标度一致时(录入数据的标度与定义列数据类型时设置的标度一致),就不会发生精度丢失。
我们常选用decimal类型,小于等于其标度的数据都能被正确录入,不会发生精度丢失,因为其是将数据以字符串的形式来存进数据库的,这就保证了精确性。但并不是说decimal就不会发生精度丢失,因为它虽然不会发生精度扩展但却会发生精度截断,例如当录入数据的标度大于列数据类型设置的标度时,依然会发生四舍五入。- 第三、其他待研究的问题
由onekbit.com 经验笔记整理发布