为什么c#不能计算数学函数的精确值

时间:2011-01-05 12:00:15

标签: c# rounding

为什么c#不能执行任何确切的操作。

Math.Pow(Math.Sqrt(2.0),2) == 2.0000000000000004

我知道双打是如何工作的,我知道舍入误差来自哪里,我知道它几乎是正确的值,而且我知道你不能在有限的双精度中存储无限数。但是为什么没有一种方法可以让c#完全计算出来,而我的计算器可以做到。

修改

这不是我的计算器,我只是举个例子:

http://www.wolframalpha.com/input/?i=Sqrt%282.000000000000000000000000000000000000000000000000000000000000000000000000000000001%29%5E2

干杯

8 个答案:

答案 0 :(得分:8)

有可能你的计算器不能完全做到 - 但它可能存储的信息多于它显示的信息,因此平方后的错误最终会超出显示的范围。要么是这种情况,要么在这种情况下它的错误就会被取消 - 但这与以正当的方式完全正确地解决它是不一样的。

另一种选择是计算器记住导致先前结果的操作,并应用代数来取消操作......但这似乎不太可能。 .NET 当然不会尝试这样做 - 它会计算中间值(两个根),然后将其平方。

如果你认为可以做得更好,我建议你尝试写出两个到(比方说)50个小数位的平方根,然后将它完全平方。看看你是否正好2 ...

答案 1 :(得分:2)

你的计算器没有准确计算它,只是圆角误差太小而没有显示。

答案 2 :(得分:1)

我相信大多数计算器都使用二进制编码的小数,这相当于C#的十进制类型(因此完全准确)。也就是说,每个字节包含数字的两位数字,数学通过对数完成。

答案 3 :(得分:0)

是什么让您认为您的计算器可以做到这一点?它几乎可以肯定显示的数字少于它计算的数字,如果你打印出2.0000000000000004只有五个小数位(例如),你就会得到'正确'的结果。

我想你可能会发现它不能。当我执行2的平方根然后将其自身相乘时,我得到1.999999998

2的平方根是像PI这样恼人的无理数之一,因此无法用正常的IEEE754双精度数甚至十进制数表示。要准确地表示它,您需要一个能够进行符号数学的系统,其中值存储为“两个平方根”,以便后续计算可以提供正确的结果。

答案 4 :(得分:0)

计算器对数字进行舍入的方式因模型而异。我的TI Voyage 200做代数来简化方程式(除其他外),但大多数计算器只会在结果上应用圆函数后显示计算的实际值的一部分。例如,您可能会发现平方根为2,计算器将存储(假设)54位小数,但只显示12位舍入小数。因此,当做2的平方根时,那么该结果的幂2会返回相同的值,因为结果是舍入的。在任何情况下,除非计算器可以保持无限小数,否则您将始终从复杂操作中获得最佳近似结果。

顺便说一下,尝试用二进制代表10.0,你会发现你无法均匀地表示它,你最终会得到(像10.00000000000..01

答案 5 :(得分:0)

您的计算器具有识别和操纵不合理输入值的方法。

例如:如果您没有明确告诉它(如ti89 / 92),2 ^(1/2)可能不会计算到计算器中的数字。

此外,计算器具有可用于操纵它们的逻辑,例如x ^(1/2)* y ^(1/2)=(x * y)^ 1/2,然后可以清洗,冲洗,重复处理非理性值的方法。

如果你要给c#一些方法来做到这一点,我想它也可以。毕竟,像mathematica这样的代数求解器并不神奇。

答案 6 :(得分:0)

之前已经提到过,但我认为你所寻找的是一个计算机代数系统。这些例子包括Maxima和Mathematica,它们的设计仅用于为数学计算提供精确的值,而不包括在CPU中。

C#等语言中的数学例程是为数值计算而设计的:如果你作为一个程序进行计算,你可能已经将它简化了,或者你只需​​要一个数值结果。

答案 7 :(得分:-1)

2.00000000000000042.都以单精度表示为10.。在您的情况下,对C#使用单精度应该给出确切的答案

对于您的其他示例,Wolfram Alpha可能会使用比机器精度更高的精度进行计算。这会增加很大的性能损失。例如,在Mathematica中,更高的精度会使计算速度慢大约300倍

k = 1000000;
vec1 = RandomReal[1, k];
vec2 = SetPrecision[vec1, 20];
AbsoluteTiming[vec1^2;]
AbsoluteTiming[vec2^2;]

这是我的机器上的0.01秒和3秒

通过在Java

中执行以下操作,您可以看到使用单精度和双精度引入的结果差异
public class Bits {
    public static void main(String[] args) {
    double a1=2.0;
    float a2=(float)2.0;
    double b1=Math.pow(Math.sqrt(a1),2);
    float b2=(float)Math.pow(Math.sqrt(a2),2);
    System.out.println(Long.toBinaryString(Double.doubleToRawLongBits(a1)));
    System.out.println(Integer.toBinaryString(Float.floatToRawIntBits(a2)));
    System.out.println(Long.toBinaryString(Double.doubleToRawLongBits(b1)));
    System.out.println(Integer.toBinaryString(Float.floatToRawIntBits(b2)));
    }
}

您可以看到单精度结果是精确的,而双精度是一位