为什么除法结果会因强制类型而异

本文关键字:类型 除法 结果 为什么 | 更新日期: 2023-09-27 18:20:57

以下是我不理解的部分代码:

byte b1 = (byte)(64 / 0.8f); // b1 is 79
int b2 = (int)(64 / 0.8f); // b2 is 79
float fl = (64 / 0.8f); // fl is 80

为什么前两个计算结果相差一个?我应该如何执行此操作,这样它既快速又正确?

EDIT:我需要字节中的结果

为什么除法结果会因强制类型而异

编辑:不完全正确,请参阅:为什么除法结果因强制类型而异?(跟进)

四舍五入问题:通过转换为byte/int,您就是在剪切小数位数。

但是64 / 0.8不应该导致任何小数点?错误:由于浮点数的性质,0.8f不能像记忆中那样精确地表示;它被存储为接近0.8f的东西(但不完全)。请参阅浮点精度示例或类似线程。因此,计算的结果不是80.0f,而是79.xxx,其中xxx接近1,但仍然不完全是1。

您可以通过在Visual Studio的即时窗口中键入以下内容来验证这一点:

(64 / 0.8f)
80.0
(64 / 0.8f) - 80
-0.0000011920929
100 * 0.8f - 80
0.0000011920929

您可以使用四舍五入来解决此问题:

byte b1 = (byte)(64 / 0.8f + 0.5f);
int b2 = (int)(64 / 0.8f + 0.5f);
float fl = (64 / 0.8f);

在这种情况下,快速和正确恐怕是不一致的。

由于我们的CPU架构中的底层表示,二进制浮点运算几乎总是会产生小错误。因此,在你的初始表达式中,你实际上得到了一个比数学正确值小一点的值。如果你期望一个整数是特定数学运算的结果,并且你得到了非常接近它的结果,你可以使用Math.Round(Double, MidpointRounding)方法来执行正确的舍入并补偿小错误(并确保你选择了你期望的MidpointRounding策略)。

简单地将结果强制转换为byteint之类的类型并不能进行舍入,它只会截断小数部分(当您将其强制转换为这些类型时,即使1.99999f也会变成1)。

十进制浮点运算速度较慢,占用内存较多,但不会导致这些错误。要执行此操作,请使用decimal文字而不是float文字(例如64 / 0.8m)。

经验法则是:

  • 如果你处理的是精确的数量(通常是人为的,比如钱),请使用decimal
  • 如果你在处理不精确的量(比如分数物理常数或无理数,比如π),请使用double
  • 如果您处理的是不精确的量(如上所述),并且为了速度可能会进一步牺牲一些准确性(例如在处理图形时),请使用float

要理解这个问题,您需要了解浮点表示和运算的基础知识。

0.8f不能在存储器中使用浮点数精确地表示。

在数学中,64/0.8等于80。在浮点运算中,60/0.8近似等于80。

将浮点转换为整数或字节时,只保留数字的整数部分。在您的情况下,浮点除法的不精确结果略小于80,因此转换为整数产生79。

如果您需要一个整数结果,我建议您将结果四舍五入,而不是强制转换。一种方法是使用以下函数,通过四舍五入到最接近的整数来转换为整数:

Convert.ToInt32(64/0.8f);