浮点算术中是否满足(1 + sqrt(2))^ 2 = 3 + 2 * sqrt(2)?

时间:2010-01-03 19:21:50

标签: c++ math floating-point precision floating-accuracy

在数学中,身份(1 + sqrt(2))^2 = 3 + 2*sqrt(2)成立。但是在浮点(IEEE 754,使用单精度,即32位)计算情况并非如此,因为sqrt(2)没有二进制的精确表示。

使用sqrt(2)的近似值为左右两侧提供不同的结果吗?如果是这样的话?对近似值求平方是否会显着降低精度?

哪个等效表达式可以得到最准确的结果?

16 个答案:

答案 0 :(得分:14)

当以IEEE-754双精度写入计算时,此标识恰好成立。原因如下:

两个正确舍入到双精度的平方根是:

sqrt(2) = 0x1.6a09e667f3bcd * 2^0

(我在这里使用十六进制,因为表示更整洁,并且更容易翻译成IEEE754格式)。如果没有发生溢出,则乘二乘以二进制浮点精确,如本例所示,所以:

2*sqrt(2) = 0x1.6a09e667f3bcd * 2^1

当我们添加三个时,我们得到:

3 + 2*sqrt(2) = 0x1.7504f333f9de68 * 2^2

然而,这不是可表示的双精度数(它是一位太宽),因此结果四舍五入到最接近的可表示数字。碰巧这个值恰好在两个可表示的数字之间,所以我们选择一个尾随零位的值:

3 + 2*sqrt(2) = 0x1.7504f333f9de6 * 2^2

现在是计算的另一面。当我们将一个加到2的双精度平方根时,得到:

1 + sqrt(2) = 0x1.3504f333f9de68 * 2^1

这也是可表示的双精度数字之间的精确中间情况,并且再次舍入到最接近的“偶数”可表示数字:

1 + sqrt(2) = 0x1.3504f333f9de6 * 2^1

当此值平方时,结果为:

(1 + sqrt(2))*(1 + sqrt(2)) = 0x1.7504f333f9de599cacbc97eaa4 * 2^2

这也不是可表示的双精度数。这个是不是一个确切的中间情况,所以它只是舍入到最近的可表示数字,即:

(1 + sqrt(2))*(1 + sqrt(2)) = 0x1.7504f333f9de6 * 2^2

摘要:以两种不同的方式计算此值会产生两种不同的舍入序列,但最终结果是相同的。然而,我们只研究了双精度计算;当使用不同的算术类型执行计算时可能不是这种情况。

然而,一般情况下,表达式3 + 2*sqrt(2)应该更准确(在它们不同的情况下),因为它只会导致任何二进制IEEE的两个舍入(平方根和加法) 754类型,而(1 + sqrt(2))*(1 + sqrt(2))导致三个舍入(平方根,加法和乘法)。还应该注意的是,两者之间的差异最多只能是一位或两位,并且可能对您的目的而言可以忽略不计。

答案 1 :(得分:11)

因为即使是0.1 + 0.2 != 0.3,你也不应指望这种复杂的等式来保持有限精度的浮点数。

由于数字被四舍五入地存储到一定数量的二进制小数,如果数字(如0.1)将具有无限多个二进制数字,则它们并不精确。因此,使用这些数字计算的结果也不准确,并且预计与计算的确切结果存在微小差异。

答案 2 :(得分:4)

  

使用近似值sqrt(2)为左侧和右侧提供不同的结果吗?如果是这样的话?

在数学上,这种相等只能起作用,因为这些数字之间存在确切的关系(它与三角形边长有关)。如果以不精确的表示形式添加模糊性,则相等性不再成立。平等是一个二元命题,所以问题不再是“哪一方是对的”,而是“这种关系真的是真的吗?”。答案是,“不,这不再是真的”。

  

对近似值求平方是否会显着降低精度?

两个浮点值上的每个操作都可能降低其准确性。某些数字的一小部分操作 - 具有精确位表示的操作 - 可以保证不会使准确性恶化。

答案 3 :(得分:4)

通常我使用[(1 + sqrt(2))^ 2] - [3 + 2 * sqrt(2)]< 0.00001在这种条件下测试相等(当然在某些情况下我忽略了这种用法)

有更好的方法吗?

评论赞赏:)

答案 4 :(得分:4)

小心的人,只依靠绝对差异可能会导致问题。它适用于1左右的小数字,它有足够的小数点,可以相差1e-5或你使用的。但想想更大的数字。他们的数字必须存储在有限的空间(尾数)。并且仅存储最高有效数字。那是什么意思?没有空间来存储可以测量1e-5等差异的数字!

总结一下,最好同时使用绝对和相对比较。

bool equal(float a, float b)
{
    if (abs(a - b) < eps)
        return true;
    if (abs(a - b) / max(abs(a), abs(b)) < eps)
        return true;
    return false;
} 

答案 5 :(得分:1)

看看光明的一面:如果你重新使用那个方程来移除sqrt s,那么既然你将处理合理大小的整数,那么方程将是浮点精确的;)< / p>

不准确通常与需要小数部分(除.5和.2的幂之外)的数字相关联。


回答你问题的另一部分:不,sqrt(2)的代表在双方都是一样的。在您开始对两侧的相同数字应用(不同)操作之前,不会引入错误(和差异):添加1与乘以2等等。

答案 6 :(得分:1)

在C ++中为浮点数定义了相等比较器的人应该被拍摄:&gt;。许多合理的语言(如SML)没有浮点数的比较运算符。我通常使用以下代码:

template < typename T >
inline bool equals( T x, T y, T precision = std::numeric_limits<T>::epsilon() ) 
{
    return abs( x - y ) <= precision;
}

注意:abs也是一个模板化函数,epsilon默认存储在外面。比较中的等于我的目的。

答案 7 :(得分:1)

双精度,(1 + sqrt(2))^2 = 3 + 2*sqrt(2)似乎成立。 请参阅C code

答案 8 :(得分:1)

我要再抛出一个想法 -

是的,确实,实数的完全相等是计算机编程中无意义的概念。

但实际数字的确切平等在我们的物理现实中是无意义的概念也是如此。

我们的物理现实中的整数是计数的结果。我们物理现实中的实数是测量的结果。并且所有测量都包括错误。要说两个物理测量具有完全相同的值是无稽之谈。最多只能进行两次物理测量,这些测量的四舍五入到与测量精度相适应的某种精度水平。

用尺子测量铅笔的长度时,你会得到一个最接近16英寸的长度。当你用一对卡尺测量它时,你会得到一个最接近千分之一英寸的长度。真实世界的测量总是包括这种舍入。当您在计算机程序中模拟真实世界的测量时,您需要执行相同的操作。

实数的平等只是数学家的一个有意义的概念。 (甚至在那里,它是一个不同的,更复杂的概念,而不是整数的平等)。

答案 9 :(得分:1)

  

sqrt(2)没有二进制的精确表示。

sqrt(2)也没有十进制,十六进制或任何其他base-n系统的精确表示;这是一个无理的数字。

sqrt(2)的唯一确切表示是sqrt(2)。或者,作为等式x 2 = 2的解决方案。

答案 10 :(得分:0)

比较浮点值时,我发现最好将差值的绝对值与给定的容差进行比较。你总是可以依靠它。

答案 11 :(得分:0)

总的来说,双方会给你不同的结果。浮点数学不满足交换和相关属性。涉及许多因素,包括编译器选项和硬件。

对于你的等式,你可以找出哪一方更准确(我的猜测乘方),但如果你决定使用不同的值,它一般不会保持,即一方可能对某些值更准确而另一方则对其他价值更准确。

在您的情况下,Squaring不应显着影响结果。

答案 12 :(得分:0)

  

使用近似值sqrt(2)为左侧和右侧提供不同的结果吗?如果是这样的话?对近似值求平方是否会显着降低精度?

加法和乘法都有误差近似。乘法是经验的,特别是当它嵌套时。

以下不是一个准确的表示,但它有助于理解我的观点:

example of addition:
(float1 * float2 + float3)
float1 * float2 + float3 + mult_approximation + add_approximation

example multiplication
(float1 * (float2 + float3))
(float1 * (float2 + float3 + add_apporiximation)
float1 * (float2 + float3) + add_approximation * float1 + mult_approximation

答案 13 :(得分:0)

这是因为表示像sqrt(x)这样的连续(无限)函数不能完全在离散(有限)状态机上完成。相反,连续函数通过泰勒级数从0到n的扩展转换为离散函数,其中n是您可以表示的最高数字(在本例中为2 ^ 32)。因为你不能在计算机上从0到无穷大的总和,你剩下一些错误。可以计算此错误,以便确定离散函数与连续函数的接近程度。

有关所涉及方程式的更多信息和相当的TeX表示: http://en.wikipedia.org/wiki/Taylor_series

答案 14 :(得分:0)

令人惊讶的是,如果由于某种原因你需要准确表示非有理数(提示:你可能没有),你可以做一些事情:连续分数算术。这个想法发生在1972年,由超级黑客Bill Gosper提出 - 谷歌起来。顺便说一下,这个想法的更高级方面是当前数学研究的问题;见例如this paper

答案 15 :(得分:0)

通常,浮点运算精确到FLT_EPSILON,即最低有效位,IEEE 32位浮点数为2 -23

另见:Wasn't the Double Type precision of 15 digits in C#?