c#中使用float计算财务值的问题
本文关键字:问题 计算 float | 更新日期: 2023-09-27 17:53:51
我正在阅读一篇关于Float和double之间差异的文章。他们给出了如下的例子:
假设你有一件价值100美元的商品,你给了10%的折扣。你的价格都是整美元,所以你使用int变量来存储价格。下面是你得到的结果:
int fullPrice = 100;
float discount = 0.1F;
Int32 finalPrice = (int)(fullPrice * (1-discount));
Console.WriteLine("The discounted price is ${0}.", finalPrice);
你猜怎么着:最终价格是89美元,而不是预期的90美元。你的客户会很高兴,但你不会。你给了他们额外1%的折扣。
在上面的例子中,他们使用fullPrice * (1-discount)
来计算最终价格。他们为什么用(1-disocunt)
?应该是fullPrice * discount
所以我的困惑是关于计算最终价格的逻辑。为什么他们用(1-discount)
而不是discount
?
最终价格是89美元,而不是预期的90美元
这是因为当0.1
是float
时,它不完全是0.1
,它稍微多一点。当你从1
中减去它做数学运算时,你得到$89.9999998509884
。转换为int
将结果截断为89
(demo)。
您可以通过为折扣使用decimal
数据类型来实现此工作。该类型可以表示0.1而没有精度损失(示例)。
为什么他们使用
(1-disocunt)
1
代表100%。折后价格为(100%-10%)=90%
这个问题实际上是关于数学的。
我们建议你有一件价值100美元的商品。
卖方给你方10%的折扣。
现在需要计算一件商品的最终价格是多少。也就是说,你应该花多少钱才能得到它?
答案是:你需要支付商品的全价减去折扣价。
100%的成本- 10%的折扣= 90%的最终价格
这就是为什么它是fullPrice * (1 - discount)
。
如果你用公式fullPrice * discount
来计算,那么这意味着花费100美元的物品将以10美元的价格出售,因为有10%的折扣——这是不正确的。实际上,可以使用公式fullPrice * discount
来计算贴现金额。
上面示例的整体逻辑没有问题,但是它的数据类型选择非常不合适。这将导致值被隐式地转换为double,从而在过程中引入轻微的舍入误差。通过转换回int,结果被截断。这极大地放大了舍入误差。
这是在编程中处理财务值时经常出现的问题的一个很好的例子:
浮点值不能很好地转换为小数。
大多数新开发人员倾向于将浮点值视为十进制分数,因为在将其转换为字符串或反之亦然时,它们大多由小数表示。这不是的情况。Float值的小数部分存储为二进制分数,如下所述。
这使得浮点值(及其计算)与它们的十进制表示略有偏差。这就是为什么下面的结果是$89,9999998509884:
double fullPrice = 100;
double discount = 0.1F;
double finalPrice = (fullPrice * (1 - discount));
Console.WriteLine("The discounted price is ${0}.", finalPrice);
有趣的事实:当使用float
作为数据类型时,上面的代码可以很好地工作,因为上述错误在本例中低于单精度值的分辨率,并且汇编代码在幕后确实使用了双精度。当结果被转换为单精度时,错误将丢失。
解决这个问题的一种方法是使用数据类型decimal
,它的构造是为了进行必须直接转换为十进制分数的计算(如金融计算):
decimal fullPrice = 100;
decimal discount = 0.1m;
decimal finalPrice = (fullPrice * (1 - discount));
Console.WriteLine("The discounted price is ${0}.", finalPrice);
另一种方法是在显示或存储浮点计算结果之前对所有结果进行舍入。对于财务计算,应该使用:
Math.Round([your value here], 2, MidpointRounding.AwayFromZero);