为什么 C# 中的浮点运算不精确?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/753948/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why is floating point arithmetic in C# imprecise?
提问by Prankster
Why does the following program print what it prints?
为什么下面的程序打印它打印的内容?
class Program
{
static void Main(string[] args)
{
float f1 = 0.09f*100f;
float f2 = 0.09f*99.999999f;
Console.WriteLine(f1 > f2);
}
}
Output is
输出是
false
采纳答案by Michael
Floating point only has so many digits of precision. If you're seeing f1 == f2, it is because any difference requires more precision than a 32-bit float can represent.
浮点数只有这么多位数的精度。如果您看到 f1 == f2,那是因为任何差异都需要比 32 位浮点数所能表示的精度更高的精度。
I recommend reading What Every Computer Scientist Should Read About Floating Point
我建议阅读每个计算机科学家应该阅读的关于浮点的内容
回答by Joel Coehoorn
The main thing is that this isn't just .Net: it's a limitation of the underlying system most every language will use to represent a floatin memory. The precision only goes so far.
主要的是,这不仅仅是 .Net:它是底层系统的一个限制,大多数语言都会用来表示内存中的浮点数。精度仅到此为止。
You can also have some fun with relatively simple numbers, when you take into account that it's not even base ten. 0.1, for example, is a repeating decimal when represented in binary.
当您考虑到它甚至不是以 10 为基数时,您也可以通过相对简单的数字获得一些乐趣。例如,0.1 在以二进制表示时是一个重复的十进制。
回答by Jim
In this particular case, it's because .09 and .999999 cannot be represented with exact precision in binary (similarly, 1/3 cannot be represented with exact precision in decimal). For example, 0.111111111111111111101111 base 2 is 0.999998986721038818359375 base 10. Adding 1 to the previous binary value, 0.11111111111111111111 base 2 is 0.99999904632568359375 base 10. There isn't a binary value for exactly 0.999999. Floating point precision is also limited by the space allocated for storing the exponent and the fractional part of the mantissa. Also, like integer types, floating point can overflow its range, although its range is larger than integer ranges.
在这种特殊情况下,这是因为 .09 和 .999999 不能用二进制精确表示(同样,1/3 不能用十进制精确表示)。例如,0.111111111111111111101111基座2是0.999998986721038818359375基座10添加1到先前二进制值,0.11111111111111111111基座2是0.99999904632568359375基座10没有用于恰好0.999999的二进制值。浮点精度还受到分配用于存储尾数的指数和小数部分的空间的限制。此外,与整数类型一样,浮点数可以溢出其范围,尽管其范围大于整数范围。
Running this bit of C++ code in the Xcode debugger,
在 Xcode 调试器中运行这段 C++ 代码,
float myFloat = 0.1;
浮动 myFloat = 0.1;
shows that myFloat gets the value 0.100000001. It is off by 0.000000001. Not a lot, but if the computation has several arithmetic operations, the imprecision can be compounded.
显示 myFloat 的值为 0.100000001。它关闭了 0.000000001。不是很多,但如果计算有多个算术运算,则不精确性可能会复杂化。
imho a very good explanation of floating point is in Chapter 14 of Introduction to Computer Organization with x86-64 Assembly Language & GNU/Linuxby Bob Plantz of California State University at Sonoma (retired) http://bob.cs.sonoma.edu/getting_book.html. The following is based on that chapter.
恕我直言,浮点的一个很好的解释是在第14章介绍计算机组织与x86-64的汇编语言和GNU / Linux的由加利福尼亚州立大学的鲍勃·Plantz在索诺玛(退休)http://bob.cs.sonoma.edu /getting_book.html。以下内容基于该章节。
Floating point is like scientific notation, where a value is stored as a mixed number greater than or equal to 1.0 and less than 2.0 (the mantissa), times another number to some power (the exponent). Floating point uses base 2 rather than base 10, but in the simple model Plantz gives, he uses base 10 for clarity's sake. Imagine a system where two positions of storage are used for the mantissa, one position is used for the sign of the exponent* (0 representing + and 1 representing -), and one position is used for the exponent. Now add 0.93 and 0.91. The answer is 1.8, not 1.84.
浮点数类似于科学记数法,其中一个值存储为大于或等于 1.0 且小于 2.0(尾数)的混合数,乘以另一个数的某个幂(指数)。浮点数使用基数 2 而不是基数 10,但在 Plantz 给出的简单模型中,为了清楚起见,他使用基数 10。想象一个系统,其中尾数使用两个存储位置,一个位置用于指数 * 的符号(0 代表 +,1 代表 -),一个位置用于指数。现在添加 0.93 和 0.91。答案是 1.8,而不是 1.84。
9311 represents 0.93, or 9.3 times 10 to the -1.
9311 代表 0.93,即 10 的 9.3 乘以 -1。
9111 represents 0.91, or 9.1 times 10 to the -1.
9111 代表 0.91,即 10 的 9.1 乘以 -1。
The exact answer is 1.84, or 1.84 times 10 to the 0, which would be 18400 if we had 5 positions, but, having only four positions, the answer is 1800, or 1.8 times 10 to the zero, or 1.8. Of course, floating point data types can use more than four positions of storage, but the number of positions is still limited.
确切的答案是 1.84,即 0 的 10 的 1.84 倍,如果我们有 5 个位置,那就是 18400,但是,只有四个位置,答案是 1800,或者 10 的 1.8 乘以零,或者 1.8。当然,浮点数据类型可以使用四个以上的存储位置,但是位置的数量还是有限的。
Not only is precision limited by space, but “an exact representation of fractional values in binary is limited to sums of inverse powers of two.” (Plantz, op. cit.).
不仅精度受到空间的限制,而且“二进制中小数值的精确表示仅限于 2 的反幂之和。” (Plantz, op. cit.)。
0.11100110 (binary) = 0.89843750 (decimal)
0.11100110(二进制)= 0.89843750(十进制)
0.11100111 (binary) = 0.90234375 (decimal)
0.11100111(二进制)= 0.90234375(十进制)
There is no exact representation of 0.9 decimal in binary. Even carrying the fraction out more places doesn't work, as you get into repeating 1100 forever on the right.
二进制中没有 0.9 十进制的精确表示。即使将分数移出更多地方也行不通,因为您会在右侧永远重复 1100。
Beginning programmers often see floating point arithmetic as more accurate than integer. It is true that even adding two very large integers can cause overflow. Multiplication makes it even more likely that the result will be very large and, thus, overflow. And when used with two integers, the / operator in C/C++ causes the fractional part to be lost. However, ... floating point representations have their own set of inaccuracies. (Plantz, op. cit.)
初级程序员通常认为浮点算术比整数更准确。确实,即使添加两个非常大的整数也会导致溢出。乘法使得结果非常大并因此溢出的可能性更大。当与两个整数一起使用时,C/C++ 中的 / 运算符会导致小数部分丢失。但是,...浮点表示有其自身的一组不准确之处。(普兰茨,前引书。)
*In floating point, both the sign of the number and the sign of the exponent are represented.
*在浮点数中,同时表示数字的符号和指数的符号。