MySQL · 答疑解惑 · 浮点型的显示问题

本文涉及的产品
云原生数据库 PolarDB 分布式版,标准版 2核8GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS SQL Server Serverless,2-4RCU 50GB 3个月
推荐场景:
简介: 背景 我们打开MySQL客户端,执行下面的SQL语句: drop table if exists t; create table t(id double)engine=innodb; insert into t values(1e-15),(1e-16); select * from t;

背景

我们打开MySQL客户端,执行下面的SQL语句:

drop table if exists t;
create table t(id double)engine=innodb;
insert into t values(1e-15),(1e-16);
select * from t;

select * from t出来的内容如下,我们看到浮点数1e-15用正常的数值来表示,1e-16用科学技术法来表示。

+-------------------+
| id                |
+-------------------+
| 0.000000000000001 |
|             1e-16 |
+-------------------+

我们知道在计算机中浮点数用来近似表示某个实数。浮点数有2种显示风格,一种是正常的表示(0.18, 2.345等),一种是科学技术法的表示(1.23e+12,2.45e-16等)。那么MySQL的浮点型在什么情况下表示成正常的实数(如0.18,2.345),什么情况下表示成科学计数法(如1.23e+12,2.45e-16)呢?下面我们进行更精确的实验以及从源码角度来解释MySQL对于浮点数的显示问题。

实验

我们用下面的SQL语句直接显示多个浮点数:

select (1e+14),(1e+15),(2.3e+14),(2.3e+15),(1e-15),(1e-16),(3.4e-15),(3.4e-16);

select出来的内容是:

+-----------------+-------+-----------------+---------+-------------------+-------+--------------------+---------+
| 1e+14           | 1e+15 | 2.3e+14         | 2.3e+15 | 1e-15             | 1e-16 | 3.4e-15            | 3.4e-16 |
+-----------------+-------+-----------------+---------+-------------------+-------+--------------------+---------+
| 100000000000000 |  1e15 | 230000000000000 |  2.3e15 | 0.000000000000001 | 1e-16 | 0.0000000000000034 | 3.4e-16 |
+-----------------+-------+-----------------+---------+-------------------+-------+--------------------+---------+

通过以上的例子再结合更多的实验我们可以看出这么一个规律:

  1. 在数值大于0时,科学计数法表示的指数小于或等于14时,select出来的是正常非科学计数法的数值;
  2. 在数值大于0时,科学计数法表示的指数大于14时,select出来的是科学计数法的数值;
  3. 当数值小于0时,科学计数法表示的指数大于或等于-15时,select出来的是正常非科学计数法的数值;
  4. 当数值小于0时,科学计数法表示的指数小于-15时,select出来的是科学计数法的数值。

另外由于上面的select并没有来自某个具体表,所以浮点数展示的规则是和存储引擎没有关系的,MySQL对于浮点数展示包装的逻辑是在server层完成的。

我们去代码里验证一下这个规律是否正确。

验证

我们可以用gdb跟到代码里面寻找这块逻辑,但是MySQL单单server层的代码也有好几万行,盲目的跟代码并不能很快的找到我们要找的位置。所以,跟代码前我们很有必要先分析一下这块逻辑会出现在什么位置。

我们知道MySQL对select的处理的大体过程是,客户端向服务端发送select,服务端解析select并把结果返回到客户端,那么这块逻辑就很有可能出现在服务端把结果送到客户端这个过程中。

最后通过跟踪代码我们发现了在MySQL将结果返回客户端的过程中,在下面这个位置的buffer->set_real对要显示的内容进行了包装,并把包装的结果放到buffer这个变量里。

sql/protocol.cc:
bool Protocol_text::store(double from, uint32 decimals, String *buffer)
{
#ifndef DBUG_OFF
  DBUG_ASSERT(field_types == 0 ||
	      field_types[field_pos] == MYSQL_TYPE_DOUBLE);
  field_pos++;
#endif
  buffer->set_real(from, decimals, thd->charset());
  return net_store_data((uchar*) buffer->ptr(), buffer->length());
}

在对set_real往更深的调用层次跟踪,我们找到了对浮点数的展示进行包装的位置:

strings/dtoa.c:
...
size_t my_gcvt(double x, my_gcvt_arg_type type, int width, char *to,
               my_bool *error)
...

通过分析my_gcvt这个函数,我们可以得出MySQL对于浮点数展示的规则。

首先我们必须知道以下这个事实(下面’f’format表示正常格式,’e’format表示科学计数法的格式):
MySQL对select出来的每一列占用的宽度是有要求的,如果浮点数在’f’format下的有效数字太多,就有可能超过最大宽度,这时若还想要用’f’format,就不得不丢失一些有效数字了。如果同样数值的’e’format不会丢失有效数字,MySQL就会把该浮点数从’f’format转为’e’format。

下面的这个if语句确定了用’f’format表示浮点数的条件。

strings/dtoa.c -> function my_gcvt

if ((have_space ||
    /*
      Not enough space, let's see if the 'f' format provides the most number
      of significant digits.
    */
     ((decpt <= width && (decpt >= -1 || (decpt == -2 && (len > 1 || !force_e_format)))) &&
       !force_e_format)) &&

     /*
       Use the 'e' format in some cases even if we have enough space for the
       'f' one. See comment for MAX_DECPT_FOR_F_FORMAT.
     */
    (!have_space || (decpt >= -MAX_DECPT_FOR_F_FORMAT + 1 &&
                     (decpt <= MAX_DECPT_FOR_F_FORMAT || len > decpt))))

代码有点乱,但是通过看注释以及上下文,我们可以分析出用’f’format表示浮点数必须同时满足2个条件:
1. 用’f’format表示浮点数不会因为宽度限制造成精度丢失;
2. 浮点数用若用’e’format表示时的指数在一个临界值范围(-15,14)内,那么就用’f’format表示。

在前面的实验中,我们给出的几个浮点数若用’f’format并不会超过列的最大宽度,即满足条件1。那么这几个浮点数用’f’format还是’e’format表示就由条件2决定了,条件2和我们在实验中看到的规律相符。

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
关系型数据库 MySQL Windows
MySQL启动时显示:发生系统错误 5。拒绝访问解决办法
MySQL启动时显示:发生系统错误 5。拒绝访问解决办法
MySQL启动时显示:发生系统错误 5。拒绝访问解决办法
|
关系型数据库 MySQL
mysql如何显示前50行?
mysql如何显示前50行?
528 0
|
关系型数据库 MySQL
解决Navicat中文乱码问题(mac版本,MySQL显示正常)
解决Navicat中文乱码问题(mac版本,MySQL显示正常)
430 0
解决Navicat中文乱码问题(mac版本,MySQL显示正常)
|
SQL 关系型数据库 MySQL
软件测试mysql面试题:如何在SQL中显示当前日期?
软件测试mysql面试题:如何在SQL中显示当前日期?
85 0
|
关系型数据库 MySQL 数据库
mysql控制台 show databases不显示结果的原因
mysql控制台 show databases不显示结果的原因
663 0
mysql控制台 show databases不显示结果的原因
|
关系型数据库 MySQL 数据库
MySQL百分比显示和显示前百分之几的方法
MySQL百分比显示和显示前百分之几的方法
443 0
MySQL百分比显示和显示前百分之几的方法
|
关系型数据库 MySQL 数据库
【MySQL作业】SELECT 数据查询——美和易思定制显示查询结果应用习题
【MySQL作业】SELECT 数据查询——美和易思定制显示查询结果应用习题
144 0
【MySQL作业】SELECT 数据查询——美和易思定制显示查询结果应用习题
|
存储 关系型数据库 MySQL
mysql加强(4)~多表查询:笛卡尔积、消除笛卡尔积操作(等值、非等值连接),内连接(隐式连接、显示连接)、外连接、自连接
mysql加强(4)~多表查询:笛卡尔积、消除笛卡尔积操作(等值、非等值连接),内连接(隐式连接、显示连接)、外连接、自连接
878 0
mysql加强(4)~多表查询:笛卡尔积、消除笛卡尔积操作(等值、非等值连接),内连接(隐式连接、显示连接)、外连接、自连接
|
存储 SQL AliSQL
【七天深入MySQL实战营】答疑汇总Day7 MySQL分支 - AliSQL 的性能优化实战
【最后一课】【MySQL分支 - AliSQL 的性能优化实战】 讲师:黄忠,阿里云资深技术专家。 课程内容:深入介绍AliSQL的几个核心通用性能优化技术;抽丝剥茧,带你整体学习从问题提出到解决方案落地全过程。 答疑汇总:特别感谢班委@陈亮 同学
|
SQL 监控 Oracle
【七天深入MySQL实战营】答疑汇总Day6 MySQL表和索引优化实战
【开营第六课】【MySQL表和索引优化实战】 讲师:田杰,阿里云高级运维专家。 课程内容:InnoDB表和索引设计最佳实践;索引设计的分析与优化。 答疑汇总:特别感谢班委@李敏 同学

相关产品

  • 云数据库 RDS MySQL 版