何时应该使用 hypot 而不是 sqrtl?

6

这里已经讨论过hypotsqrt慢,因为它处理当两个输入非常大或非常小时的情况,使得sqrt会错误地返回inf或0。

然而,根据一些测试hypot甚至比首先转换为更高精度然后进行平凡计算和转换更慢。

那么,在什么情况下应该使用hypot?只有当没有更大的浮点类型并且溢出是一个问题时吗?

编辑:sqrtlhypot在输入a=1,b=1.8e-8时有所不同,但sqrtl返回更准确的结果:

hypot 1.0
sqrtl 1.000000000000000222044604925031
exact 1.000000000000000161999999999999986878000000000002125764000...
1+eps 1.0000000000000002220446049250313080847263336181640625

在结果中添加eps的确切行数在此处显示。

2个回答

6

double hypot(double x, double y) 函数计算直角三角形斜边的长度,其两条直角边分别为 xy,或者计算点 (x, y) 到原点的距离。使用这个函数而不是直接使用公式 sqrt(x * x + y * y) 是明智的,因为误差要小得多。实际上,对于某些参数值,平方可能会导致精度丢失(如果值太小,则将其平方产生 0),或者如果值太大则产生无限结果。使用直接公式可能会产生不正确的结果,甚至超出了预期范围: max(|x|, |y|) <= hypot(x, y) <= sqrt(2) * max(|x|, |y|)

hypot() 使用替代公式来避免这些异常情况,但会带来一定的性能损失,但如果您知道参数不会导致精度丢失或无限结果,并且您需要额外的速度而牺牲一些正确性,您可以使用简单的公式 sqrt(x * x + y * y)

作为经验法则,如果 xy 都为零或者它们的绝对值介于 1e-100 到 1e+100 之间,并且 1e-4 <= |x|/|y| <= 1e4,那么使用 sqrt 应该是可以的。

在您的例子中,b 相对于 a 很小,导致完全失去精度,因为 b*ba*a 小得很多,以至于 a*a + b*b 无法与 a*a 区分开来。使用 long double 作为中间结果能够获得足够的额外精度,使得 a*a + b*b 能够用足够的精度表示,以便 sqrt 计算出有意义的结果。但是由于 sqrt(1 + epsilon) 大约等于 1 + epsilon/2,结果仍然可用。


如何判断x <= hypot() <= y是否成立?这不是一个平均。 - l4m2
如果 b 是原来的十分之一,即 b=1.8e-9,那么 (real)hypot(a,b) 就是 1+1.6199999999999999986878000000000000021257639999999999e-18,返回 1 是正确的。 - l4m2
@l4m2:没错。因此,在大多数情况下,long double 的增加精度似乎已经足够了。 - chqrlie
虽然与理想的 hypot 仍有一些差异(请参见“将 eps 添加到结果的确切行”),但这仍然是一种解决方案。 - l4m2

2
这是因为你在比较不应该比较的东西... hypot 并没有将值转换成更高的精度,计算平方根再转回低精度。相反,hypot 使用专门的算法确保即使数字对于给定的精度非常大或非常小,计算结果也会返回良好的结果。还有一个 hypotl 可以计算长双精度浮点数的长度,而使用 sqrtl 无法计算得很好。此外,long double 可能与 double 完全相同类型...

1
hypot() 的主要作用不是解决大数的问题,而是解决精度损失的问题。当你平方一个数时,需要两倍于原数的位数来保存结果,取根运算时,结果的很多位数依赖于这些额外的位数,特别是在值比较小的情况下。因此,如果您有 float 值,则将它们扩展为 double,平方、相加、求根,最后将结果转换回 float 是一种有效的方法。如果您的输入已经是 double,并且需要高精度,则需要使用 hypot() - cmaster - reinstate monica
@cmaster-reinstatemonica 您是正确的,我过于简化了,因为似乎即使在提到的文章中也已经正确说明了,但 OP 并没有很好地理解问题。 - Jean-Baptiste Yunès
@cmaster-reinstatemonica 当使用 sqrt 时,它总会失去精度。是否有输入使它们的输出不同?另外,将 float 扩展为 double 并使用 sqrt 比使用 hypotf 更快。 - l4m2
我知道它们使用不同的方法,但如果它们的作用相同,为什么我需要使用它呢?另外,“有一个 hypotl 函数可以计算长双精度浮点数的长度,而使用 sqrtl 函数可能无法正确计算。此外,长双精度浮点数可能与双精度浮点数完全相同...”被认为是“当没有更大的浮点类型并且溢出成为问题时”。 - l4m2
1
答案的关键部分是:“而且,long double 可能与 double 完全相同的类型”-换句话说,回答问题标题所问的,“当您希望您的代码具有可移植性时”。 - R.. GitHub STOP HELPING ICE
这个回答并没有回答所问的问题,即何时应该使用hypot而不是其他方法,比如使用更高精度或范围的直接计算。 - Eric Postpischil

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接