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

  1. +-------------------+
  2. | id |
  3. +-------------------+
  4. | 0.000000000000001 |
  5. | 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出来的内容是:

  1. +-----------------+-------+-----------------+---------+-------------------+-------+--------------------+---------+
  2. | 1e+14 | 1e+15 | 2.3e+14 | 2.3e+15 | 1e-15 | 1e-16 | 3.4e-15 | 3.4e-16 |
  3. +-----------------+-------+-----------------+---------+-------------------+-------+--------------------+---------+
  4. | 100000000000000 | 1e15 | 230000000000000 | 2.3e15 | 0.000000000000001 | 1e-16 | 0.0000000000000034 | 3.4e-16 |
  5. +-----------------+-------+-----------------+---------+-------------------+-------+--------------------+---------+
  1. 在数值大于0时,科学计数法表示的指数小于或等于14时,select出来的是正常非科学计数法的数值;
  2. 在数值大于0时,科学计数法表示的指数大于14时,select出来的是科学计数法的数值;
  3. 当数值小于0时,科学计数法表示的指数小于-15时,select出来的是科学计数法的数值。

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

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

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

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

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

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

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

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

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

  1. 用’f’format表示浮点数不会因为宽度限制造成精度丢失;

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