阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

Double类型精度问题引起的错误

165次阅读
没有评论

共计 2993 个字符,预计需要花费 8 分钟才能阅读完成。

场景说明

研发同事让把某个 double 类型字段的值四舍五入保留 2 位小数,mysql 中 round(col,2)可以实现四舍五入并且保留 2 位小数,但是神奇的事情发生了:发现有的四舍五入是正确的,而有的不是我们想要的结果,如下:简单模拟此场景:

yujx>drop table dd;
yujx>create table dd (a double);
yujx>insert into dd values(956.745),(231.34243252),(321.43534),(5464.446);
yujx>select a,round(a,2) from dd;
+————–+————+
| a            | round(a,2) |
+————–+————+
|      956.745 |    956.74 |     #可以看到并不是我们期望的 956.75
| 231.34243252 |    231.34 |
|    321.43534 |    321.44 |
|    5464.446 |    5464.45 |
+————–+————+
4 rows in set (0.00 sec)

如上,有的是正确的,有的不是我们期望的值,那么问题来了,为什么呢?

通过询问和网上搜索后,发现可能是因为 double 的精度问题导致的

查阅 MySQL 官当关于 doubleFloatDECIMAL, NUMERIC 类型的介绍,如下:

FLOAT, DOUBLE

# 为了说明问题,摘了官当的部分内容,关于更详细的介绍请直接查看官当。

12.2.3 Floating-Point Types (Approximate Value) – FLOAT, DOUBLE

# 可以看到标题已经指出 Float 和 double 是近似值

The FLOAT and DOUBLE types represent approximate numeric data values. MySQL uses four bytes for single-precision values and eight bytes for double-precision values.

 

Because floating-point values are approximate and not stored as exact values, attempts to treat them as exact in comparisons may lead to problems. They are also subject to platform or implementation dependencies. For more information, see Section B.5.5.8,“Problems with Floating-Point Values

因为 Float、Double 存储的是一个近似值而不是确切的值,试图使用它们存储一个确切的值可以会导致问题。它们依赖于不同平台和不同实现方式,而官当在章节 Section B.5.5.8,“Problems with Floating-Point Values 中举例说明了此问题

参考:http://dev.mysql.com/doc/refman/5.7/en/floating-point-types.html

“Problems with Floating-Point Values”

B.5.4.8 Problems with Floating-Point Values

Floating-point numbers sometimes cause confusion because they are approximate and not stored as exact values. A floating-point value as written in an SQL statement may not be the same as the value represented internally. Attempts to treat floating-point values as exact in comparisons may lead to problems. They are also subject to platform or implementation dependencies. The FLOAT and DOUBLE data types are subject to these issues. For DECIMALcolumns, MySQL performs operations with a precision of 65 decimal digits, which should solve most common inaccuracy problems.

由于浮点数存储的是近似值而不是确切的值,某些时候可能导致混乱。一个浮点数值在 SQL 语句作为内部表示的值可能不同。试图使用 float、double 来存储确切的值可能会出现问题,他们也依赖不同平台和实现方式。而对应 DECIMAL 类型,MySQL 作为 65 位精度进行操作,可以解决此类精度问题。

如下例子证明使用 Double 运算遇到的异常错误

Double 类型精度问题引起的错误

此结果是错误的,尽管前 5 条的 a 和 b 的值看起来是不满足 a <>b 条件的。

Double 类型精度问题引起的错误

此现象取决于各种因素,如计算机架构、编译器版本或者优化级别等。例如,不同的 CPU 评估的浮点数不同

如果将字段 d1 和 d2 改成 DECIMAL 类型,将不会存在此问题。如下:

Double 类型精度问题引起的错误

综上,如果想精确的存储浮点数值,应该使用 DECIMAL. 比如金额等。

DECIMAL, NUMERIC

11.2.2 Fixed-Point Types (Exact Value) – DECIMAL, NUMERIC

The DECIMAL and NUMERIC types store exact numeric data values. These types are used when it

is important to preserve exact precision, for example with monetary data. In MySQL, NUMERIC is

implemented as DECIMAL。

DECIMAL 和 NUMBERIC 存储的是确切的数值,使用它们可以保证精确度,例如用于存储金额数据。在 MySQL 中,NUMBERIC 和 DECIMAL 以同样的类型实现。

double 在其他平台

double 类型不只是在 MySQL 中存在精度错误的问题,在 Oracle、Java 等平台同样存在此问题,如下:

Double 类型精度问题引起的错误

简单的 0.2+0.4, 但是返回的结果不是 0.6。

回到问题开始

如果换成 DECIMAL 类型,round 的结果将是正确的,如下:

yujx>drop table dd;
yujx>create table dd (a double,b decimal(30,10));
yujx> insert into dd

values(956.745,956.745),(231.34243252,231.34243252),(321.43534,321.43534),

(5464.446,5464.446);
yujx>select a,round(a,2) from dd;

Double 类型精度问题引起的错误

综上

只为说明一个问题,如果想要确切的存储小数(例如,金额等),建议使用 DECIMAL 类型,而不是 DOUBLE、float 类型。

本文永久更新链接地址:http://www.linuxidc.com/Linux/2016-05/131110.htm

正文完
星哥玩云-微信公众号
post-qrcode
 0
星锅
版权声明:本站原创文章,由 星锅 于2022-01-22发表,共计2993字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中