提问人:Prankster 提问时间:4/16/2009 最后编辑:Mark LakataPrankster 更新时间:1/28/2022 访问量:29810
为什么 C# 中的浮点运算不精确?
Why is floating point arithmetic in C# imprecise?
问:
为什么以下程序会打印它打印的内容?
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 位浮点数更精确的精度。
我推荐阅读《每个计算机科学家都应该读的关于浮点的知识》
评论
最主要的是,这不仅仅是 .Net:它是大多数每种语言用来表示内存中浮点数的底层系统的限制。精度仅到此为止。
你也可以用相对简单的数字来获得一些乐趣,当你考虑到它甚至不是十进制时。例如,0.1(1/10th)在以二进制表示时是重复的小数,就像 1/3rd 在以十进制表示时一样。
在这种特殊情况下,这是因为 .09 和 .999999 不能以二进制的精确精度表示(同样,1/3 不能以十进制的精确精度表示)。例如,0.1111111111111111111111011111 以 2 为基数 0.999998986721038818359375 以 10 为基数。将 1 添加到前一个二进制值,0.111111111111111111111111 以 2 为基数 2 是以 0.999999904632568359375 为基数 10。没有正好 0.999999 的二进制值。浮点精度还受到为存储尾数的指数和小数部分而分配的空间的限制。此外,与整数类型一样,浮点型可以溢出其范围,尽管其范围大于整数范围。
在 Xcode 调试器中运行这一段 C++ 代码,
浮点数 myFloat = 0.1;
显示 myFloat 获取值 0.100000001。它偏离了 0.000000001。不是很多,但如果计算有多个算术运算,那么不精确性可能会加剧。
恕我直言,对浮点的一个很好的解释是在加州州立大学索诺玛分校的 Bob Plantz(已退休)http://bob.cs.sonoma.edu/getting_book.html 的 Introduction to Computer Organization with x86-64 Assembly Language & GNU/Linux 的第 14 章中。以下内容以该章为基础。
浮点数类似于科学记数法,其中值存储为大于或等于 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 乘以 10 到 -1。
确切的答案是 1.84,即 1.84 乘以 10 到 0,如果我们有 5 个位置,这将是 18400,但是,只有四个位置,答案是 1800,或 1.8 乘以 10 到 0,或 1.8。当然,浮点数据类型可以使用四个以上的存储位置,但位置的数量仍然有限。
精度不仅受到空间的限制,而且“二进制小数值的精确表示仅限于 2 的反幂之和。“(普朗茨,同前)。
0.11100110(二进制)= 0.89843750(十进制)
0.11100111(二进制)= 0.90234375(十进制)
二进制中没有 0.9 小数的精确表示。即使将分数带到更多的地方也行不通,因为您会在右边永远重复 1100。
初级程序员通常认为浮点运算更多 比整数准确。的确,即使加两个非常大 整数可能会导致溢出。乘法使它更有可能 结果将非常大,因此会溢出。使用时 对于两个整数,C/C++ 中的 / 运算符会导致小数部分 迷失。然而。。。浮点表示有自己的 一组不准确之处。“(普朗茨,同前。
*在浮点数中,表示数字的符号和指数的符号。
上一个:阶乘后有多少个尾随零?
评论