C#浮点舍入行为

时间:2014-02-15 08:37:14

标签: c# .net floating-point

我一直在尝试解决由浮点运算引起的错误,并将其简化为一段简单的代码,导致我不理解的行为:

float one = 1;
float three = 3;

float result = one / three;
Console.WriteLine(result); // prints 0.33333

double back = three * result;

if (back > 1.0)
    Console.WriteLine("larger than one");
else if (back < 1.0)
    Console.WriteLine("less than one");
else
    Console.WriteLine("exactly one");

结果四舍五入到0.33333,我希望回到小于1,但输出“大于一”。

有人能解释一下这里发生了什么吗?

2 个答案:

答案 0 :(得分:4)

当我尝试上面的代码时,我发现了

float result = one / three;

语句将result的值评估为0.333333343而不是0.33333,但控制台将其打印为0.33333,然后执行以下语句

double back = three * result;

它将back评估为1.0000000298023224,显然大于1,这就是为什么你“大于一个”

答案 1 :(得分:4)

使用IEEE 754舍入,让我们看看发生了什么。

在IEEE 754单精度浮点中,有限数的值由以下内容决定:

  

-1 符号×2 指数×(1 +尾数×2 -23

其中

  • sign 如果为正则为0,否则为1;
  • exponent 是介于-126和127之间的值(-127和128是特殊的);和
  • 尾数是介于0和8388607之间的值(因为它是23位整数)。

如果我们将 sign 替换为0且 exponent 替换为-2,那么我们保证介于0.25和0.5之间的值。为什么呢?

  

1×2 -2

是¼。

的价值
  

1 +尾数×2 -23

保证在1和2之间,因此我们的符号和指数已经排序。


继续,我们可以很快地计算出有两个值可以用作尾数值:2796202和2796203。

将每个值替换为,我们得到以下两个值(一个更低,一个更高):

  • 0.333333313465118408203125(尾数 = 2796202)
  • 0.3333333432674407958984375(尾数 = 2796203)

精确值(最多22位)的二进制表示为:

1010101010101010101010...

由于下一个数字为1,这意味着该值将向上,而不是向下。出于这个原因,较高的误差比较低的误差小:

  • 0.333333313465118408203125 - ⅓≑-1.987×10 -8
  • 0.3333333432674407958984375 - ⅓≑9.934×10 -9

并且因为它大于精确值,所以当乘以它时将大于1.这就是为什么它使用最初出现的值 - 二进制舍入有时与十进制舍入的方向相反。