我面临着一个非常奇怪的错误?现在在mysql + php上。 是一个简单的选择,在下面的例子中,我使用多个字段来尝试解释我的问题:
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,对吧?好吧,是的,没有。
查询结果:
答案 0 :(得分:8)
问题是如何存储DOUBLE和FLOAT值。
有可能(并且可能)像11.5或22.475这样的值存储在近似值中,如11.499999999999~或22.475000000000000001,因此某些计算或舍入可能会导致错误的结果。
将浮点值存储到 DECIMAL coulmn类型中总是更好,其中值与所有十进制数字完全一致地存储,并且不是近似值。
答案 1 :(得分:5)
精确的数字文字(例如所有四个示例中的1.15
和后两个中的11.5
)使用MySQL的DECIMAL
类型encoded。
在前两个示例中,将field
(类型DOUBLE
)与此类文字相乘的结果是另一个DOUBLE
,使用8-byte {{3}存储浮点表示(即IEEE Standard)。但是,在此表示中13.225
编码为0x402A733333333333
,其位代表:
Sign : 0b0 Biased exponent: 0b10000000010 = 1026 (representation includes bias of +1023, therefore exp = 3) Significand : 0b[1.]1010011100110011001100110011001100110011001100110011 = [1.]6531249999999999555910790149937383830547332763671875 ^ hidden bit, not stored in binary representation
这相当于:
(-1)^0 * 1.6531249999999999555910790149937383830547332763671875 * 2^3 = 13.2249999999999996447286321199499070644378662109375000
因此,将此结果四舍五入到小数点后两位会产生13.22
而不是13.23
。
使用两个DECIMAL
类型进行乘法运算,例如后两个示例中使用的两个文字,会产生另一个DECIMAL
。在此表示中,13.225
以binary64格式进行编码,具有精确的精度,因此舍入操作会按预期生成13.23
。
如binary-coded decimal中所述:
浮点数有时会引起混淆,因为它们是近似值而不是存储为精确值。在SQL语句中写入的浮点值可能与内部表示的值不同。尝试将浮点值视为比较中的精确值可能会导致问题。它们还受平台或实现依赖性的影响。 MySQL manual和
FLOAT
数据类型受这些问题的影响。对于DOUBLE
列,MySQL执行精度为65位十进制数的运算,这可以解决最常见的不准确问题。
如果您精确准确,DECIMAL
可能更符合您的要求。如果您需要DOUBLE
的范围/效果但仍希望获得所需的舍入结果,则可以在舍入之前将DECIMAL
乘以DECIMAL
的结果。