为什么19.9变成了19.89,不靠谱的 double 类型
今天财务告诉我线上有个¥19.9的商品,在用户付款时变成了¥19.89,致使公司损失了N个1分钱。我第一反应是不是不靠谱的财付通造成的,因为财付通需要我们把商品金额转换成“分”,这样我们在向财付通传递数据时必须事先对商品价格*100,打电话给财付通,对账后发现数据确实是我们这边就传错了。好吧,来看看我们的问题在那里,查看代码发现有类似这样的一行代码:
int a = (int)(fee *100);
由于我们的fee不会超过2个小数,因此这行代码从表面看不会有什么问题。但感觉肯定是这个转换有精度损失,从而将1990变成了1989 。正在奇怪的时候,发现fee在其他地方被定义成了double类型,就是这个精度不够高的double类型在转换成int时损失了精度。真是太奇怪了,因为如果我们将fee强制转换成精度更高的decimal类型,就不会有问题,其结果就是正确的1990而不是1989。这种精度从表面是看不出来的。
写一段测试代码:
int a = (int)(19.9 *100); //19.9默认是double类型
int b = (int)(19.9M *100 ); //将19.9转换成decimal类型
Console.WriteLine(a); //输出:1989
Console.WriteLine(b); //输出:1990
怪不得,即使微软都建议进行财务计算时最好使用精度更高的decimal类型。因为double太不靠谱,用它计算的结果会“超出你的期望”。写到这里,其实我也只能怀疑是精度的原因了,因为从测试代码来看,确实很诡异。
当我写完以上的文字,正准备关闭程序的时候,突然想起来财务说过,我们以前有¥9.9的商品都没有出现过这个问题,啊!为什么会这样。难道是程序抽疯了??好吧继续写测试:
int a = (int)(9.9*100);
int b = (int)(9.9M *100);
Console.WriteLine(a); //输出:990 为什么不是想象的:989 ?
Console.WriteLine(b); //输出:990
继续测试:
int a = (int)(29.9*100); //输出:2990 为什么不是:2989 ?
int b = (int)(29.9M *100); //输出:2990
Console.WriteLine(a);
Console.WriteLine(b);
好吧,先打住吧,我都要疯了,难道我关于精度的怀疑是错误的?
继续加大测试范围:
double k = 0.0;
for (int i = 0; i < 50; i++)
{
int a = (int)((k) * 100);
int b = (int)((decimal)(k) * 100);
if (a != b)
{
Console.WriteLine(a);
Console.WriteLine(b);
Console.WriteLine("————");
}
k += 0.9;
}
输出结果还暂时还看不出是什么规律,求微软的大侠来解释哈。
该文章同时发布在这里:http://jasonjiang.me/archives/44