6

It's already discussed here that hypot is slower than sqrt because it handles cases when the two inputs are huge or tiny so that sqrt would wrongly return inf or 0.

However, according to some test, hypot is even slower than first convert to higher precision and then do trivial calculation and convert back.

So, in what case should I use hypot? Only when there's no larger float type and overflow is an issue?

EDIT: sqrtl and hypot differ on input a=1,b=1.8e-8, but sqrtl returns more accurate result:

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

Exact lines where an eps is added to result is shown here

l4m2
  • 1,157
  • 5
  • 17

2 Answers2

6

The function double hypot(double x, double y) computes the length of the hypotenuse of a right triangle with sides of x and y, or the distance of the point (x, y) from the origin. Using this function instead of the direct formula sqrt(x * x + y * y) is wise, since the error is much smaller. Indeed for some argument values, squaring can cause either a loss of precision (if the value is too small, squaring it produces 0), or an infinite result of the value is too large. Using the direct formula can produce incorrect results, not even inside the expected range: max(|x|, |y|) <= hypot(x, y) <= sqrt(2) * max(|x|, |y|).

hypot() uses alternative formulae to avoid these pathological cases, at some performance cost, but if you know your arguments are not causing a loss of precision nor an infinite result, and you need extra speed at the expense of correctness, you can use the simple formula sqrt(x * x + y * y).

As a rule of thumb, if x and y are zero or they have an absolute value between 1e-100 and 1e+100 and 1e-4 <= |x|/|y| <= 1e4, sqrt should be fine.

In your example, b is very small compared to a, causing a complete loss of precision because b*b is so much smaller than a*a that a*a + b*b cannot be distinguished from a*a. Using the long double for the intermediary result gains you enough extra precision for a*a + b*b to be represented with enough precision for sqrt to compute a meaningful result. But since sqrt(1 + epsilon) is approximately 1 + epsilon/2, the result is usable anyway.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • How `x <= hypot() <= y`? This is not an average – l4m2 Apr 13 '20 at 17:50
  • If `b` is 10 times smaller, i.e. `b=1.8e-9`, then (real)`hypot(a,b)` is `1+1.6199999999999999986878000000000000021257639999999999e-18`, and it's correct to return 1 – l4m2 Apr 14 '20 at 11:41
  • @l4m2: correct. so the increased precision of `long double` seems sufficient in most cases. – chqrlie Apr 14 '20 at 14:42
  • It's still some difference to an ideal hypot, though (see `Exact lines where an eps is added to result`) – l4m2 Apr 14 '20 at 17:40
2

This is because your are comparing things that you should not... hypot is not converting to higher precision, computing the square root and converting back to lower precision. hypot uses a dedicated algorithm to ensure that the computation will return a good result, even if numbers are huge or tiny for a given precision. There is an hypotl to compute the length for long doubles that you cannot compute well using sqrtl. And, long double may be the exact same type than double...

Jean-Baptiste Yunès
  • 34,548
  • 4
  • 48
  • 69
  • 1
    Large magnitudes are not the main problem that `hypot()` solves, loss of precision is. When you square numbers, you need twice as much digits of precision to hold the result, and when you take the root, a good part of the result's digits depends on these additional bits of precision. Especially if the values are small. So, if you have `float` values, extending them to `double`, squaring them, adding them, taking the root, and finally casting the result back to `float` is a valid approach. You need `hypot()` if your inputs are already `double` and you need the precision. – cmaster - reinstate monica Apr 13 '20 at 15:32
  • @cmaster-reinstatemonica You are right, I oversimplified because it seems that even that has been correctly stated in the mentioned articles, OP didn't understand well the problem. – Jean-Baptiste Yunès Apr 13 '20 at 15:34
  • @cmaster-reinstatemonica When doing `sqrt` it anyway lose precision. Is there input that make their output different? P.S. extending `float` to `double` to `sqrt` is also faster than `hypotf` – l4m2 Apr 13 '20 at 15:37
  • I know they use different method, but why do I need to use it if they work same? Also `There is an hypotl to compute the length for long doubles that you cannot compute well using sqrtl. And, long double may be the exact same type than double... ` is considered as `when there's no larger float type and overflow is an issue` – l4m2 Apr 13 '20 at 15:38
  • 1
    The key part of the answer is "And, long double may be the exact same type than double" - in other words, to answer the question title as-asked, "when you want you code to be portable". – R.. GitHub STOP HELPING ICE Apr 13 '20 at 15:49
  • This answer does not answer the question asked, which is when one should use `hypot` versus another method such as direct calculations with higher precision or range. – Eric Postpischil Apr 13 '20 at 17:01