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 ?

c#中使用float计算财务值的问题

最终价格是89美元,而不是预期的90美元

这是因为当0.1float时,它不完全是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);