The function hypot
offers another approximation of the mathematical expression √(x2 + y2), just like the floating-point expression sqrt(x*x + y*y)
is an approximation of this same mathematical expression.
The function hypot
is recommended because it solves very noticeable defects that are present in the floating-point computation sqrt(x*x + y*y)
with very large or small values. For instance, if x
is only a bit larger than the square root of the maximum finite floating-point value, sqrt(x*x + y*y)
always produces +inf
because x*x
produces +inf
.
Compare:
>>> x, y = 95E200, 168E200
>>> sqrt(x*x + y*y), hypot(x, y)
(inf, 1.93e+202)
>>> z, t = 95E-200, 168E-200
>>> sqrt(z*z + t*t), hypot(z, t)
(0.0, 1.93e-198)
For these two (respectively very large and very small) pairs of inputs, hypot
is doing fine, whereas sqrt(x*x + y*y)
is catastrophically wrong.
When the naïve version sqrt(x*x + y*y)
works reasonably well (when the values x
and y
are neither very large nor very small), it can be may be more or less accurate than the function hypot
depending on the values of x
and y
. They can both be expected to produce a result that is a few ULPs away from the mathematical result. But since they are different approximations obtained by different methods, they may differ (in the worst case by twice “a few ULPs”).
One typical implementation for hypot(x, y)
is first to swap x
and y
if necessary so that x
has the largest magnitude, and then compute x * sqrt(1 + (y/x)*(y/x))
. This solves the problem with x*x
overflowing. As a side-effect, it means that even when there is no overflow, the result is slightly different from sqrt(x*x + y*y)
.
Note that it's normal that sqrt(x*x + y*y)
is more precise when you apply it to small integers (as you do in your test): when x
and y
are small integers, x*x
and y*y
and their sum can be computed exactly as floating-point values. If this sum is the square of an integer, the floating-point function sqrt
can only compute this integer. In short, in this scenario the computations, despite being floating-point, are exact from beginning to end. In contrast, the typical hypot
implementation above starts by computing x/y
(in your test, 95.0/168.0
), and this result is not in general representable exactly as a floating-point value. The first step already incurs an approximation, and this approximation can result in the final result being wrong (as it is in your test)!
There is no standard algorithm for hypot
: it is only expected to compute a good approximation of the mathematical expression √(x2 + y2) while avoiding the overflow and underflow problems. This article shows different implementations, and points out that the popular implementation that I mentioned sacrifices accuracy to avoid overflow and underflow (but the article also provides a floating-point implementation for hypot
that is more accurate than sqrt(x*x + y*y)
even where sqrt(x*x + y*y)
works).