6

我正面临一个非常奇怪的¿错误?现在在 mysql+php 上。是一个简单的选择,在以下示例中,我使用多个字段来尝试解释我的问题:

  • “字段”是 11.5
  • $phpvar 是 1.15

MySQL查询:

select round(field * " . $phpvar . " ,2) as a1, 
       round(field * 1.15 ,2) as a2, 
       round(11.5 * " . $phpvar . " ,2) as a3, 
       round(11.5 * 1.15 ,2) as a4, 
       field * " . $phpvar . " as a5 
from ...

好的,我正在尝试获得 13.23。"field" * $phpvar = 13.225,所以使用 round(13.225,2) 我应该得到 13.23,对吗?好吧,是的,也不是。

查询结果:

  • a1 [round(field * " . $phpvar . " ,2)] => 13.22
  • a2 [round(field * 1.15 ,2)] => 13.22
  • a3 [round(11.5 * " . $phpvar . " ,2)] => 13.23
  • a4 [round(11.5 * 1.15 ,2)] => 13.23
  • a5 [field * " . $phpvar . "] => 13.225 (无圆)

我错过了什么?怎么可能,在使用“场”时,我的结果是假的?

4

2 回答 2

9

问题是如何存储 DOUBLE 和 FLOAT 值。

11.5 或 22.475 之类的值有可能(并且很可能)存储为近似值,例如 11.499999999999~ 或 22.475000000000000001,因此某些计算或舍入可能会导致不正确的结果。

将浮点值存储到DECIMAL coulmn 类型总是更好,其中该值与所有十进制数字完全存储并且不近似。

于 2012-05-04T10:20:36.697 回答
5

确切的数字文字(例如1.15所有四个示例中的 the 和11.5后两个示例中的 the )使用 MySQL 的类型进行编码。DECIMAL

field在您的前两个示例中,将(of type DOUBLE) 与此类文字相乘的结果是 another DOUBLE,它使用8 字节 IEEE 标准浮点表示(即binary64)存储。但是,在此表示中13.225 编码为0x402A733333333333,其位表示:

符号           :0b0

有偏指数:0b10000000010
               = 1026(表示包括 +1023 的偏差,因此 exp = 3)

有效    数字:0b[1.]1010011100110011001100110011001100110011001100110011
               = [1.]6531249999999999555910790149937383830547332763671875
                    ^ 隐藏位,不以二进制表示形式存储

这相当于:

  (-1)^ 0 * 1.6531249999999999555910790149937383830547332763671875 * 2^ 3 
=          13.2249999999999996447286321199499070644378662109375000

因此,将此结果四舍五入到小数点后两位得出13.22not 13.23

使用两种类型执行乘法运算DECIMAL,例如后两个示例中使用的两个文字,会产生另一个DECIMAL. 在此表示中,13.225二进制编码的十进制格式进行编码,具有精确的精度,因此舍入运算结果13.23符合预期。

MySQL 手册中所述:

浮点数有时会引起混淆,因为它们是近似值而不是存储为精确值。SQL 语句中写入的浮点值可能与内部表示的值不同。尝试在比较中将浮点值视为精确可能会导致问题。它们还受平台或实现依赖关系的影响。FLOATDOUBLE数据类型受这些问题的影响。对于DECIMAL列,MySQL 以 65 位十进制数字的精度执行操作,这应该可以解决最常见的不准确问题。

如果您追求精确的精度,DECIMAL可能更适合您的要求。如果您需要的范围/性能DOUBLE但仍希望获得所需的舍入结果,您可以在舍入之前CONVERT将乘法的结果。DECIMAL

于 2012-05-04T11:20:15.590 回答