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

25 浏览
0 Comments

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

为什么以下程序打印出它打印的内容?

代码:

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

输出是:

false

0
0 Comments

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

在这种特殊情况下,是因为0.09和0.999999无法以二进制精确表示(类似地,1/3无法以十进制精确表示)。例如,0.111111111111111111101111二进制表示为0.999998986721038818359375十进制。将1添加到前一个二进制值,0.11111111111111111111二进制表示为0.99999904632568359375十进制。没有一个二进制值可以精确表示0.999999。浮点精度还受到存储指数和尾数的分数部分的空间限制。与整数类型一样,浮点也可以超出其范围,尽管其范围比整数范围更大。

在Xcode调试器中运行以下C ++代码:

float myFloat = 0.1;

可以看到myFloat的值为0.100000001。它偏离了0.000000001。虽然不多,但如果计算有多个算术操作,不精确性可能会被累积。

浮点数的一个很好的解释在加利福尼亚州索诺玛州立大学(退休)的Bob Plantz的《Introduction to Computer Organization with x86-64 Assembly Language & 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乘以10的-1次方。

精确答案是1.84,或1.84乘以10的0次方,如果我们有5个位置,那么结果将为18400,但是由于只有四个位置,答案是1800,即1.8乘以10的零次方,即1.8。当然,浮点数据类型可以使用多个存储位置,但是位置的数量仍然有限。

精度不仅受到空间的限制,而且“在二进制中,对分数值的精确表示仅限于二的倒数之和。”(Plantz,op. cit.)。

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

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

在二进制中没有0.9十进制的精确表示。即使将小数部分延长到更多位,也无法解决这个问题,因为右侧会无限重复1100。

初学者通常认为浮点运算比整数运算更准确。确实,即使将两个非常大的整数相加也可能导致溢出。乘法使结果更可能非常大,从而溢出。而在C / C ++中,当与两个整数一起使用时,/运算符会丢失小数部分。但是,浮点表示也有其自身的不准确之处(Plantz,op. cit.)。

*在浮点数中,数的符号和指数的符号都有所表示。

0
0 Comments

浮点数在C#中不精确的原因是什么?

浮点数在C#中不精确是由于底层系统的限制所导致的,这个限制也适用于大多数编程语言中用于表示浮点数的方式。浮点数的精度有限。

即使是相对简单的数字也可能会出现问题,因为浮点数不是基于十进制表示的。例如,0.1(1/10)在二进制表示中是一个重复的十进制数,就像1/3在十进制表示中一样是一个重复的十进制数。

解决方法:

1. 使用decimal类型代替float或double类型。decimal类型提供了更高的精度,适用于需要更精确计算的场景。

2. 避免直接比较浮点数的相等性。由于浮点数的精度问题,直接比较两个浮点数的值可能会出现错误的结果。可以使用误差范围或使用Decimal.Equals方法来比较浮点数的相等性。

3. 尽量避免在循环中进行浮点数运算。由于浮点数的精度问题,多次进行浮点数运算可能会导致累积误差。可以考虑使用整数运算或使用BigDecimal类进行精确计算。

4. 了解浮点数的特性和限制。了解浮点数在二进制表示中的特点,以及由于精度限制可能导致的误差,有助于更好地理解和处理浮点数计算中的问题。

浮点数在C#中不精确是由于底层系统的限制所导致的。为了解决这个问题,可以使用decimal类型代替float或double类型,避免直接比较浮点数的相等性,避免在循环中进行浮点数运算,并了解浮点数的特性和限制。

0
0 Comments

浮点数只有有限的精度。如果你看到f1 == f2,那是因为任何差异都需要比32位浮点数能表示的更多的精度。如果想要更好的精度,可以使用双精度double类型。双精度double类型只是将问题推迟了一些位数。如果想要更高的精度,可以使用QD库中的四倍精度quad doubles。这个库中还包含了一个任意精度的库。

0