17

I've seen a number of questions about simulations and animations in javascript, which often involve calculating the hypotenuse:

hypot = Math.sqrt(x*x + y*y);

Since cartesian coordinates are the weapon of choice in most of these engines, these calculations are needed to find the distance between pairs of points, etc. So any speedup in calculating the hypotenuse could be a great help to many projects.

To that end, can you see a faster method than the simple implementation above? I found an approximation which was marginally faster in Chrome, but turned out to be much slower in Firefox, based on this approximation function in SuperCollider.

Edit 2015-08-15: I've switched the accepted answer to being the Math.hypot one; I suspect the pragmatic approach at present would be to use Math.hypot or a synthesized hypot function if not available, and to compare against the square (per sch's answer) if that is sufficient and Math.hypot is not available.

Phil H
  • 18,593
  • 6
  • 62
  • 99
  • 3
    you could always use [some 0x5f3759df magic](http://en.wikipedia.org/wiki/Fast_inverse_square_root) – violet313 Apr 13 '12 at 12:30
  • 1
    It's noble of you to want to speed up every script that uses the Pythagorean formula. However, I don't think a general solution exists to make the formula faster (otherwise we wouldn't be using the 2,500 year old version). Instead of trying to make the formula faster, try to refactor your code so that you use the formula less, and only after you've demonstrated that the formula is the bottleneck in your code's performance. – Kevin Apr 13 '12 at 12:35
  • @Kevin: In C or some other language with lower overheads, there are indeed approximations that speed things up. Whether an approximation is useful depends on the accuracy needed by a model, but for game physics it would generally be worthwhile if it makes gameplay more fluid. – Phil H Apr 13 '12 at 12:51
  • @PhilH, I agree with you on both counts, that faster approximations exist and that using them is worthwhile if it improves the user experience. But you need to investigate each use of the formula on a case-by-case basis, to judge whether a particular approximation would be appropriate and worthwhile. I'm just saying that no universal panacea exists, and optimization should only be done after you know that you need it. – Kevin Apr 13 '12 at 13:07
  • @violet313 AFAIK, JS uses doubles, not floats, and then JS isn't C(++), so no pointer cast tricks. – Alexey Frunze Apr 13 '12 at 13:22
  • See [this Wikipedia article](http://en.wikipedia.org/wiki/Methods_of_computing_square_roots) for some methods of square root calculation. – Alexey Frunze Apr 13 '12 at 13:25
  • 1
    @[Alex](http://stackoverflow.com/users/968261/alex) yes. & i feared an unconstructive flagging ~but then it's such a beautiful hack ;) – violet313 Apr 13 '12 at 15:08

5 Answers5

18

Often, you don't need to compute the square root and hypot^2 = x*x + y*y is enough. This is the case for example if you want to compare the distances and don't need the actual values.

sch
  • 27,108
  • 3
  • 65
  • 82
10

In ECMAScript ES6 you can use Math.hypot:

// ES5 support

Math.hypot = Math.hypot || function(x, y){ return Math.sqrt(x*x + y*y) }

var x = 3, y = 4;

document.write(Math.hypot(x, y))

Edit: You can run this test on a blank tab, are 2 million operations with both methods, the results are very good, it is 24% faster.

var i, tmp, x = 55, y = 66, end, ini = performance.now();

// Math.sqrt operation
i = 0;
ini = performance.now();
tmp = 0;
while(i++ < 2000000){
    tmp += Math.sqrt(x*x + y*y)
}
end = performance.now();
console.log(tmp, "Math.sqrt operation: " + (end - ini) + " ms");

// Math.hypot

i = 0;
ini = performance.now();
tmp = 0;
while(i++ < 2000000){
    tmp += Math.hypot(x, y)
}
end = performance.now();

console.log(tmp, "Math.hypot: " + (end - ini) + " ms");

Note: In this test, it's used ES6's Math.hypot.

enter image description here

Mingye Wang
  • 791
  • 5
  • 26
  • @PhilH Edit, I tested the operation with Math.sqrt and Math.hypot, you can see it. In this test the original function of Math.hypot is used – Walter Chapilliquen - wZVanG Jun 24 '15 at 23:26
  • 3
    `hypot` appears consistently slower on Chrome 62. I tried to put it in a strict function to minimize outside interference (https://gist.github.com/anonymous/159187ac9a8d3caf97737cd9cf551c1a), but sqrt remains the faster operation. Perhaps they added some protection against overflow/underflow at some point? – Mingye Wang Sep 27 '17 at 21:22
  • On my chrome version 68.0.3440.106 and 16 GB RAM, 2.7 GHz Intel Core i7, Math,sqrt is about 25% faster. Ex. 77 vs 302 ms – zyrup Aug 15 '18 at 19:01
8

An important point that many do not know:

hypot = Math.sqrt(x*x + y*y);

That works in theory, but in practice it may fail. If x is so large that x*x overflows, the code will produce an infinite result.

Here’s how to compute sqrt(xx + yy) without risking overflow.

max = maximum(|x|, |y|)
min = minimum(|x|, |y|)
r = min / max
return max*sqrt(1 + r*r)

Reference and complete text: John D. Cook - http://www.johndcook.com/blog/2010/06/02/whats-so-hard-about-finding-a-hypotenuse/

Paul Richter
  • 10,414
  • 8
  • 47
  • 77
Henry
  • 353
  • 2
  • 14
2

Performance of Math.sqrt() and Math.hypot() depends on javascript running environment.

I just run this code in Node.js, Chrome and Firefox:

let z = performance.now();
for (let i = 0; i < 1000000000; i++) {
    Math.hypot(i, i);
}
console.log(performance.now() - z);

z = performance.now();
for (let i = 0; i < 1000000000; i++) {
    Math.sqrt(i * i + i * i);
}
console.log(performance.now() - z);

with the following results:

Node.js v14.15.4:

24394.656100034714
419.97210001945496

Chrome 88.0.4324.150:

26474.060000036843
422.13000007905066

Firefox 85.0.2:

423
419

which means V8 has awful implementation of Math.hypot(). Actually I wouldn't be much surprised if it also depended on CPU architecture/model.

Note that in my example I feed integer numbers to Math.sqrt() and Math.hypot(). Another test revealed that with floating-point numbers Node.js runs test code 20% slower than on integers. In Chrome Math.sqrt() performance is exactly the same and Math.hypot() runs like 3% slower. Firefox: no difference.

yes yes
  • 21
  • 1
1

You can look to equality of x and y. If are equals you can calculate hypotenuse as (x + y)/sqrt(2) where sqrt(2)is a constant.

So this method can be used for case where x = y. For other cases it can be used with maximum imprecision of ~41%. This is a big error. But when you specify allowable error limits you can use this method. For example, if define allowable error to 5% you can get that b must be between 0.515*a and 1.942*a.

So if you don't need perfect imprecision of your calculations, you can improve performance of calculations with range of values.

By analogy you can look to equality of x or y to zero. And with some accuracy calculate hypotenuse more faster for this cases.

P.S. I've read about this in the one russian article.

Alexander Burov
  • 829
  • 8
  • 11