不要盲目的说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.000001 2.110005 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在计算前一定要对取到的数据的精度做处理,确保和存储的精度一致。而存储的精度也和需要的精度一致,不要长也不要 短。

  

参考:https://www.jianshu.com/p/c81edc59546c

  

mysql数据精度丢失问题深入探讨的更多相关文章

  1. mysql 字符串转数据丢失精度,mysql转换丢失精度,mysql CAST 丢失精度

    mysql 字符串转数据丢失精度,mysql转换丢失精度,mysql CAST 丢失精度 =============================== ©Copyright 蕃薯耀 2017年9月1 ...

  2. springboot 解决Jackson导致Long型数据精度丢失问题

    代码中注入一个bean即可: /** * 解决Jackson导致Long型数据精度丢失问题 * * @return */ @Bean("jackson2ObjectMapperBuilder ...

  3. 找不到mysql.sock,mysql.sock丢失问题解决方法

    Can 't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock '(2) "; 是你的mysql ...

  4. Mysql ibdata 丢失或损坏如何通过frm&ibd 恢复数据

    mysql存储在磁盘中,各种天灾人祸都会导致数据丢失.大公司的时候我们常常需要做好数据冷热备,对于小公司来说要做好所有数据备份需要支出大量的成本,很多公司也是不现实的.万一还没有做好备份,数据被误删除 ...

  5. 关于ORACLE的硬解析和软解析与MySQL的查询缓存query_cache探讨

    今天在项目中探讨到Oracle对于SQL语句的解析方法以及MySQL相应的处理方法: --------------------------------------------------------- ...

  6. 启动mysql错误解决方案,学会查看错误日志:mysql.sock丢失,mysqld_safe启动报错

    本人还是个菜鸟,下面是我的经验之谈,能解决一些问题,有不对的地方,敬请斧正. 我的是CentOS6.3+MySQL5.1.57. 重启了一次服务器后,使用> mysql -u root -p登陆 ...

  7. MySQL更新丢失

    MySQL多主结构(比如: MGR Multi Master模式),如果多主都可以写的话,很有可能出现更新丢失的情况. 定义:  T1时间,T2时间,T3时间 定义:  事务A, 事务B Node1节 ...

  8. ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)-mysql.sock丢失解决方案

    我们的LAMP是搭建在UBUNTU 12.04 LTS上的. LAMP是通过编译的方式进来安装的. 在一次处理意外挂机时由于未知的原因在重启后发现无法连接数据库了, 在打开网站时出现如下的的提示: E ...

  9. MySql权限丢失问题解决

    参考文章 [mysql] root权限丢失恢复 完全菜鸟教程 今天用root账户登录到mysql后, show databases 命令返回的只有两个表, 然后使用 use database 命令提示 ...

随机推荐

  1. postgresql获取表最后更新时间(通过发布订阅机制将消息发送给应用程序)

    一.创建测试表 CREATE TABLE weather( city ), temp_lo int, --最低温度 temp_hi int, --最高温度 prcp real, --湿度 date d ...

  2. 【转】gl_NormalMatrix

    关于为什么对法线变换时要使用变换矩阵的转置的逆,而不能像变换顶点那样直接使用变换矩阵 https://www.cnblogs.com/bigdudu/articles/4191042.html 注意文 ...

  3. 十八、浏览器不能打开jenkins报告,报错Opening Robot Framework report failed

    解决方案一:(推荐) 打开jenkins----系统管理---输入:  System.setProperty("hudson.model.DirectoryBrowserSupport.CS ...

  4. C++中的集合和字典

    https://blog.csdn.net/sinat_39037640/article/details/74080509

  5. SynchronousQueue 源码分析

    SynchronousQueue SynchronousQueue 能解决什么问题?什么时候使用 SynchronousQueue? 1)SynchronousQueue 没有任何内部容量. 2)Sy ...

  6. MyBatis系列:一、入门

    MyBatis无需我介绍,本系列文章是纯干货,没有一点废话. 1.创建一个maven项目,引入mysql的驱动和mybatis的maven引用 <dependency> <group ...

  7. 阶段1 语言基础+高级_1-3-Java语言高级_06-File类与IO流_07 缓冲流_4_缓冲流的效率测试_复制文件

    把之前文件复制的代码复制到这里 一个字节一个字节的读取,复制文件 byte数组的形式 缓冲流测试 数组缓冲

  8. 阶段1 语言基础+高级_1-3-Java语言高级_06-File类与IO流_04 IO字节流_1_IO概述(概念&分类)

  9. dataframe指定位置插入行

    1 loc( ) 函数可以定位行后,并直接赋值插入. 如下可见loc函数对直接改变原来行的值 df = pd.DataFrame({ '动物' : ['狗','猫','兔'], '数量' : [ 3, ...

  10. 只有一个form 的程序, onactivate 只触发一次。

    https://blog.csdn.net/saint13/article/details/454615 Form的onActivate事件 2005年08月15日 01:08:00 阅读数:3406 ...