我正在阅读一篇与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
?
答案 0 :(得分:1)
最终价格是89美元,而不是预期的90美元
那是因为当0.1
为float
时,它不完全是0.1
,而是更多一点。当你从1
中减去它来进行数学运算时,得到$89.9999998509884
。转换为int
会将结果截断为89
(demo)。
您可以使用decimal
数据类型进行折扣。此类型可以表示0.1而没有精度损失(demo)。
为什么他们使用
(1-disocunt)
1
代表100%。折扣后的价格是(100%-10%)= 90%
答案 1 :(得分:0)
这个问题实际上是关于数学的。
我们建议您购买一件价值100美元的商品 卖家为您提供折扣 - 10%。
现在您需要计算物品的最终价格。 即,你应该花多少钱才能得到它?
答案:您需要支付商品的全价减去折扣价 100%的成本 - 10%的折扣= 90%的最终价格
这就是fullPrice * (1 - discount)
的原因
如果您使用公式fullPrice * discount
进行计算,则意味着100美元的商品将因10%的折扣而以10美元的价格出售 - 这是不正确的。实际上,此公式{{1}}可用于计算折扣金额。
答案 2 :(得分:0)
上述示例的整体逻辑没有任何问题,但它确实有非常不幸的数据类型选择。这导致值被隐式转换为double,在过程中引入了轻微的舍入误差。通过强制转换为int,结果将被截断。这极大地放大了舍入误差。
这是在编程中处理财务价值时经常出现的问题的一个很好的例子:
浮动值不能很好地反映小数。
大多数新开发人员倾向于将浮点值视为小数,因为当将它们转换为字符串时,它们主要由这些表示,反之亦然。 情况并非如此。 Float值的小数部分存储为二进制分数,如同here所示。
这使浮点值(及其计算)从它们的十进制表示略微偏斜。这就是为什么以下结果为$ 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);