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

使用近似值的值 sqrt(2) 为左手和右侧提供不同的结果吗?如果是这样,为什么?平方近似值是否会大大降低准确性?

那么哪个等效表达式给出了最准确的结果?

有帮助吗?

解决方案

当按IEEE-754双重精度编写计算时,这种身份恰好存在。为什么:

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

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

(我在这里使用十六进制,因为表示形式更加整洁,并且转换为IEEEE754格式要容易得多)。如果没有发生溢出,则在二进制浮点点中精确地乘以两个,如在这种情况下,因此::

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

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

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

但是,这不是代表的双重精确编号(它太宽了),因此结果被舍入到最近的代表数字。碰巧的是,这个值正好在两个代表数字之间的一半,因此我们选择一个尾部零位的值:

3 + 2*sqrt(2) = 0x1.7504f333f9de6 * 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)) 产生三个圆形(平方根,添加和乘法)。还应注意,两者之间的差异最多将是一个或两个位,并且出于您的目的可能会忽略不可。

其他提示

自从偶数 0.1 + 0.2 != 0.3 您不应该指望如此复杂的平等性来保持有限的精度浮点数。

由于数字被舍入到一定数量的二进制小数中,如果数字(例如0.1)具有无限的二进制数字,则不确定。因此,使用这些数字的计算结果也不准确,并且预期计算结果的小差异很小。

那么,使用SQRT(2)的近似值为左侧和右侧提供不同的结果吗?如果是这样,为什么?

从数学上讲,这种平等仅由于这些数字之间存在确切的关系而起作用(这与三角形的两侧长度有关)。如果您以不精确表示的形式添加模糊性,则相等性不再正确。平等是二进制主张,因此问题不再是“哪一方是正确的”,而是“这种关系完全是正确的?”。答案是:“不,不再是真的”。

平方近似值是否会大大降低准确性?

在两个浮点值上的每个操作都可能降低其准确性。一定数量的一小部分操作(具有确切位表示的操作)可以保证不会使准确性恶化。

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

有没有更好的办法?

评论非常感谢:)

当心的人,只依靠绝对差异会导致问题。它适用于1个左右的少量数字,该数字有足够的小数点能够以1E-5的差异或您使用的方式有所不同。但是请考虑更大的数字。他们的数字必须存储在有限的空间中(Mantissa)。而且只有最重要的数字存储。这意味着什么?没有剩下的空间可以存储数字,可以测量诸如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;
} 

看看明亮的一面:如果您重新设计该方程式以删除 sqrtS,然后由于您将处理合理尺寸的整数,因此等式将在浮点上精确;)

不准确的数字通常与需要小数分数(0.5和.2的功率)的数字相关。


回答您问题的另一部分:不,代表 sqrt(2) 确实在双方都是一样的。直到您开始将(不同)操作应用于双方相同数字之前,才引入错误(和差异):添加1 vs.乘以2,等等。

定义C ++中浮子的平等比较器的人应拍摄:>。许多合理的语言(如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默认值存储在外部。比较中的平均值是为了我的目的。

以双重精度, (1 + sqrt(2))^2 = 3 + 2*sqrt(2) 似乎持有。看 C代码.

我要再提出一个主意 -

是的,确实如此,实数的确切平等是计算机编程中毫无意义的概念。

但是,实际上,实数的精确平等是我们物理现实中毫无意义的概念。

我们物理现实中的整数是计数的结果。我们物理现实中的实数是测量结果。所有测量都包括错误。说两个物理测量的值完全相同的值是胡说八道。充其量,两个物理测量值舍入到一定程度的精度,适合于测量能力的准确性。

当您用标尺测量铅笔的长度时,您将获得最接近16英寸的长度。当您用一对卡尺进行测量时,您将获得最接近1000英寸的长度。现实世界的测量始终包括这种舍入。当您模拟计算机程序中的现实世界测量值时,您需要做同样的事情。

实数平等是一个有意义的概念,仅对数学家而言。 (即使在那里,这也是一个与整数平等的不同,更复杂的概念)。

SQRT(2)在二进制中没有确切的表示。

SQRT(2)在十进制,十六进制或任何其他碱基N系统中都没有精确的表示。这是一个不合理的数字。

SQRT(2)的唯一确切表示是SQRT(2)。或者作为方程式x的解决方案2 = 2.

在比较浮点值时,我发现最好将差异的绝对值与给定的公差进行比较。您可以随时指望这一点。

那么,使用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

这是因为代表连续的(无限)函数,例如SQRT(X),无法准确地在离散(有限)状态机上完成。取而代之的是,连续函数通过泰勒级数从0到n的扩展转换为离散函数,其中n是您可以表示的最高数字(在这种情况下为2^32)。因为您无法在计算机上从0到Infinity的总和,所以您剩下的剩余错误。可以计算此错误,以便您可以确定离散函数与连续函数的距离。

有关所涉及方程式的更多信息和漂亮的TEX表示:http://en.wikipedia.org/wiki/taylor_series

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

对于您的方程式,您可能可以找出哪个方面更准确(我的猜测乘以一侧),但是如果您决定使用不同的值,则一般不会容纳,IE一侧可能更准确,而对于某些值另一面对于其他值更准确。

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

令人惊讶的是,如果出于某种原因需要准确表示非理性数字(提示:您可能不这样做),那么您可以做一些事情:持续的分数算术。这个想法归1972年,这是由于超级黑客比尔·戈斯珀(Bill Gosper) - 谷歌搜索。顺便说一句,这个想法的更先进的方面是当前数学研究的问题。参见例如 这张纸.

通常,浮点操作精确到flt_epsilon,即,在最不重要的位置,对于IEEE 32位的浮子为2−23.

另请参阅: C#中的15位数字的双重精度不是吗?

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top