为什么C#中的浮点运算不精确?

为什么下面的程序打印什么打印?

class Program { static void Main(string[] args) { float f1 = 0.09f*100f; float f2 = 0.09f*99.999999f; Console.WriteLine(f1 > f2); } } 

输出是

 false 

浮点只有这么多的精度数字。 如果你看到f1 == f2,这是因为任何差别都需要比32位浮点数更高的精度。

我build议阅读每个计算机科学家应该阅读关于浮点

最主要的是这不仅仅是.Net:它是底层系统的一个限制, 大部分语言将用来表示内存中的浮点数 。 精度只有这么远。

当你考虑到它甚至不是基数的时候,你也可以用相对简单的数字来获得一些乐趣。 例如,0.1是以二进制表示的重复小数。

在这种情况下,这是因为.09和.999999不能用二进制精确表示(类似地,1/3不能用十进制精确表示)。 例如,0.111111111111111111101111基数2是0.999998986721038818359375基数10.将1加到前面的二进制数值中,0.11111111111111111111基数2是0.99999904632568359375基数10.没有二进制数值,精确到0.999999。 浮点精度也受限于分配用于存储指数和尾数小数部分的空间。 而且,和整数types一样,虽然范围大于整数范围,浮点可以溢出它的范围。

在Xcodedebugging器中运行这一点C ++代码,

float myFloat = 0.1;

显示myFloat的值为0.100000001。 它是0.000000001。 不是很多,但是如果计算有几个算术运算,那么不精确可以被复合。

关于浮点的一个非常好的解释是在加州州立大学索诺玛分校Bob Plantz(退役)的x86-64汇编语言和GNU / Linux计算机组织导论的第14章(退役) http://bob.cs.sonoma.edu /getting_book.html 。 以下是基于这一章。

浮点类似于科学记数法,其中一个值被存储为大于或等于1.0且小于2.0(尾数)的混合数字,将另一个数字乘以某个幂(指数)。 浮点数使用基数2而不是基数10,但在Plantz给出的简单模型中,为了清晰起见,他使用了基数10。 设想一个系统,其中两个存储位置用于尾数,一个位置用于指数符号*(0代表+和1代表 – ),一个位置用于指数。 现在加0.93和0.91。 答案是1.8,而不是1.84。

9311表示0.93,或者9.3的10倍到-1。

9111代表0.91,或者是9.1的9.1倍。

确切的答案是1.84或1.84倍10到0,如果我们有5个职位,这将是18400,但只有四个职位,答案是1800,或1.8的10倍到零,或1.8。 当然,浮点数据types可以使用四个以上的存储位置,但是位置数量还是有限的。

精确度不仅受空间限制,而且“二进制小数值的精确表示限于两个反函数的和”(Plantz,前引书)。

0.11100110(二进制)= 0.89843750(十进制)

0.11100111(二进制)= 0.90234375(十进制)

二进制中没有精确的0.9十进制表示。 即使把这些零碎的东西拿出来也是行不通的,因为你会在右边重复1100次。

开始的程序员经常看到浮点运算比整数更精确。 确实,即使添加两个非常大的整数也会导致溢出。 乘法使得结果更可能是非常大的,因此,溢出。 并且当使用两个整数时,C / C ++中的/运算符会导致小数部分丢失。 但是,浮点表示有其自己的一套不准确。 (Plantz,同上)

*在浮点数中,数字的符号和指数的符号都被表示出来。