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

简介: 背景 我们打开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和我们在实验中看到的规律相符。

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
目录
相关文章
|
5月前
|
运维 算法 机器人
阿里云AnalyticDB具身智能方案:破解机器人仿真数据、算力与运维之困
本文将介绍阿里云瑶池旗下的云原生数据仓库AnalyticDB MySQL推出的全托管云上仿真解决方案,方案采用云原生架构,为开发者提供从开发环境、仿真计算到数据管理的全链路支持。
|
2月前
|
存储 人工智能 OLAP
AI Agent越用越笨?阿里云AnalyticDB「AI上下文工程」一招破解!
AI上下文工程是优化大模型交互的系统化框架,通过管理指令、记忆、知识库等上下文要素,解决信息缺失、长度溢出与上下文失效等问题。依托AnalyticDB等技术,实现上下文的采集、存储、组装与调度,提升AI Agent的准确性与协同效率,助力企业构建高效、稳定的智能应用。
|
3月前
|
存储 人工智能 关系型数据库
阿里云AnalyticDB for PostgreSQL 入选VLDB 2025:统一架构破局HTAP,Beam+Laser引擎赋能Data+AI融合新范式
在数据驱动与人工智能深度融合的时代,企业对数据仓库的需求早已超越“查得快”这一基础能力。面对传统数仓挑战,阿里云瑶池数据库AnalyticDB for PostgreSQL(简称ADB-PG)创新性地构建了统一架构下的Shared-Nothing与Shared-Storage双模融合体系,并自主研发Beam混合存储引擎与Laser向量化执行引擎,全面解决HTAP场景下性能、弹性、成本与实时性的矛盾。 近日,相关研究成果发表于在英国伦敦召开的数据库领域顶级会议 VLDB 2025,标志着中国自研云数仓技术再次登上国际舞台。
404 0
|
4月前
|
存储 人工智能 分布式计算
数据不用搬,AI直接炼!阿里云AnalyticDB AI数据湖仓一站式融合AI+BI
阿里云瑶池旗下的云原生数据仓库AnalyticDB MySQL版(以下简称ADB)诞生于高性能实时数仓时代,实现了PB级结构化数据的高效处理和分析。在前几年,为拥抱大数据的浪潮,ADB从传统数仓拓展到数据湖仓,支持Paimon/Iceberg/Delta Lake/Hudi湖格式,为开放的数据湖提供数据库级别的性能、可靠性和管理能力,从而更好地服务以SQL为核心的大规模数据处理和BI分析,奠定了坚实的湖仓一体基础。
|
5月前
|
存储 人工智能 关系型数据库
从“听指令”到“当参谋”,阿里云AnalyticDB GraphRAG如何让AI开窍
阿里云瑶池旗下的云原生数据仓库 AnalyticDB PostgreSQL 版 GraphRAG 技术,创新融合知识图谱动态推理+向量语义检索,通过实体关系映射与多跳路径优化,构建可应对复杂场景的决策引擎。本文将通过家电故障诊断和医疗预问诊两大高价值场景,解析其如何实现从“被动应答”到“主动决策”的跨越。
|
6月前
|
分布式计算 运维 监控
Fusion 引擎赋能:流利说如何用阿里云 Serverless Spark 实现数仓计算加速
本文介绍了流利说与阿里云合作,利用EMR Serverless Spark优化数据处理的全过程。流利说是科技驱动的教育公司,通过AI技术提升用户英语水平。原有架构存在资源管理、成本和性能等痛点,采用EMR Serverless Spark后,实现弹性资源管理、按需计费及性能优化。方案涵盖数据采集、存储、计算到查询的完整能力,支持多种接入方式与高效调度。迁移后任务耗时减少40%,失败率降低80%,成本下降30%。未来将深化合作,探索更多行业解决方案。
392 1
|
6月前
|
SQL 存储 缓存
海量数据分页查询效率低?一文解析阿里云AnalyticDB深分页优化方案
本文介绍了AnalyticDB(简称ADB)针对深分页问题的优化方案。深分页是指从海量数据中获取靠后页码的数据,常导致性能下降。ADB通过快照缓存技术解决此问题:首次查询生成结果集快照并缓存,后续分页请求直接读取缓存数据。该方案在数据导出、全量结果分页展示及业务报表并发控制等场景下表现出色。测试结果显示,相比普通分页查询,开启深分页优化后查询RT提升102倍,CPU使用率显著降低,峰值内存减少至原方案的几分之一。实际应用中,某互联网金融客户典型慢查询从30秒优化至0.5秒,性能提升60+倍。
493 1
|
7月前
|
SQL 关系型数据库 MySQL
客户说|保险极客引入阿里云AnalyticDB,多业务场景效率大幅提升
“通过引入AnalyticDB,我们在复杂数据查询和实时同步方面取得了显著突破,其分布式、弹性与云计算的优势得以充分体现,帮助企业快速响应业务变化,实现降本增效。AnalyticDB的卓越表现保障了保险极客数据服务的品质和效率。”

相关产品

  • 云数据库 RDS MySQL 版
  • 推荐镜像

    更多