我一直在尝试解决由浮点运算引起的错误,并将其简化为一段简单的代码,导致我不理解的行为:
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,但输出“大于一”。
有人能解释一下这里发生了什么吗?
答案 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且 exponent 替换为-2,那么我们保证介于0.25和0.5之间的值。为什么呢?
1×2 -2
是¼。
的价值1 +尾数×2 -23
保证在1和2之间,因此我们的符号和指数已经排序。
继续,我们可以很快地计算出有两个值可以用作尾数值:2796202和2796203。
将每个值替换为,我们得到以下两个值(一个更低,一个更高):
精确值(最多22位)的二进制表示为:
1010101010101010101010...
由于下一个数字为1
,这意味着该值将向上,而不是向下。出于这个原因,较高的误差比较低的误差小:
并且因为它大于精确值,所以当乘以它时将大于1.这就是为什么它使用最初出现的值 - 二进制舍入有时与十进制舍入的方向相反。