What is Math.hypot() in JavaScript? A Complete Guide
I was calculating the distance between two points in JavaScript. I wrote the obvious formula:
function distance(x, y) { return Math.sqrt(x * x + y * y);}
console.log(distance(3, 4)); // 5 - looks good!Then I tried it with larger numbers and got Infinity. My data was lost. The calculation overflowed before Math.sqrt was even called.
The Problem: Overflow in Naive Distance Calculation
I was processing GPS coordinates and scientific data when my distance calculations started returning Infinity:
function distance(x, y) { return Math.sqrt(x * x + y * y);}
// Works fine for small numbersdistance(3, 4); // 5
// FAILS for large numbersdistance(1e200, 1e200); // Infinity - should be 1.414e200distance(1e154, 1e154); // Infinity - intermediate overflow
// FAILS for very small numbersdistance(1e-200, 1e-200); // 0 - should be 1.414e-200The problem is x * x for x = 1e200 produces 1e400, which exceeds JavaScript’s maximum safe floating-point value (Number.MAX_VALUE ≈ 1.8e308).
Even worse, I realized I had similar code scattered throughout my codebase:
// In collision detectionconst dist = Math.sqrt(dx*dx + dy*dy);
// In 3D renderingconst depth = Math.sqrt(x*x + y*y + z*z);
// In color distance calculationconst colorDiff = Math.sqrt(r*r + g*g + b*b);
// All of these can overflow!The Solution: Math.hypot()
JavaScript has a built-in function that handles this automatically:
// Built into JavaScript since ES6Math.hypot(3, 4); // 5Math.hypot(1e200, 1e200); // 1.414e200 - correct!Math.hypot(1e-200, 1e-200); // 1.414e-200 - correct!
// Works with any number of arguments (n-dimensional distance)Math.hypot(3, 4, 12); // 13 (3D distance)Math.hypot(1, 2, 3, 4); // 5.477... (4D distance)Math.hypot(); // 0 (no arguments)Math.hypot(5); // 5 (one argument)Why Math.hypot() Works: The Algorithm
The naive approach squares all values first, then takes the square root:
Step 1: Square each value x * x + y * x For x = 1e200: x * x = 1e400
Step 2: Sum and sqrt Problem: 1e400 overflows before sqrt is called Result: InfinityMath.hypot() uses a smarter approach:
Step 1: Find the maximum absolute value M M = max(|x|, |y|, ...)
Step 2: If M is 0 or Infinity, handle edge cases
Step 3: Scale all values by 1/M x_scaled = x / M y_scaled = y / M
Step 4: Calculate in scaled space sum = x_scaled^2 + y_scaled^2 + ...
Step 5: Scale back result = M * sqrt(sum)
Why this works: - All scaled values are <= 1 - No overflow in intermediate squares - Precision is maintainedHere’s a simplified implementation:
function myHypot(...args) { // Filter out non-finite values const values = args.filter(v => isFinite(v));
// Handle empty or zero case if (values.length === 0) return 0;
// Find maximum absolute value let max = 0; for (const v of values) { const abs = Math.abs(v); if (abs > max) max = abs; }
// Edge case if (max === 0) return 0;
// Scale, sum, and unscale let sum = 0; for (const v of values) { const scaled = v / max; sum += scaled * scaled; }
return max * Math.sqrt(sum);}
// Test itmyHypot(1e200, 1e200); // 1.414e200 - works!When to Use Math.hypot()
I found these patterns where Math.hypot() is the right choice:
1. 2D Distance Calculation
// Collision detectionfunction checkCollision(obj1, obj2) { const dx = obj2.x - obj1.x; const dy = obj2.y - obj1.y; const distance = Math.hypot(dx, dy); return distance < (obj1.radius + obj2.radius);}2. 3D Distance Calculation
// 3D point distancefunction distance3D(p1, p2) { return Math.hypot( p2.x - p1.x, p2.y - p1.y, p2.z - p1.z );}
// Depth calculation in renderingfunction calculateDepth(camera, point) { return Math.hypot( point.x - camera.x, point.y - camera.y, point.z - camera.z );}3. Vector Magnitude
// Vector operationsfunction magnitude(vector) { return Math.hypot(...Object.values(vector));}
// Normalize a vectorfunction normalize(vector) { const mag = magnitude(vector); const result = {}; for (const [key, value] of Object.entries(vector)) { result[key] = value / mag; } return result;}4. Color Difference (RGB)
// Color similarityfunction colorDistance(color1, color2) { return Math.hypot( color2.r - color1.r, color2.g - color1.g, color2.b - color1.b );}Edge Cases and Gotchas
Math.hypot() handles edge cases gracefully:
// NaN handlingMath.hypot(3, NaN); // NaNMath.hypot(NaN, 4); // NaNMath.hypot(3, 4, NaN); // NaN
// Infinity handlingMath.hypot(3, Infinity); // InfinityMath.hypot(Infinity, 4); // Infinity
// Mixed valuesMath.hypot(-3, 4); // 5 (absolute values used)Math.hypot(0, 0, 0); // 0Math.hypot(); // 0 (empty call)
// Very large arrayMath.hypot(...new Array(10000).fill(1)); // 100 (works fine)Performance Comparison
I benchmarked the naive approach vs Math.hypot():
function benchmark() { const iterations = 10_000_000;
// Test 1: Naive approach (small numbers) console.time('Naive (small)'); for (let i = 0; i < iterations; i++) { Math.sqrt(3*3 + 4*4); } console.timeEnd('Naive (small)');
// Test 2: Math.hypot (small numbers) console.time('hypot (small)'); for (let i = 0; i < iterations; i++) { Math.hypot(3, 4); } console.timeEnd('hypot (small)');
// Test 3: Large numbers (only hypot works) console.time('hypot (large)'); for (let i = 0; i < iterations; i++) { Math.hypot(1e200, 1e200); } console.timeEnd('hypot (large)');}
benchmark();Results:
Naive (small): ~45mshypot (small): ~55mshypot (large): ~60msMath.hypot() is slightly slower for small numbers due to the scaling algorithm, but the difference is negligible. The correctness gain is worth it.
Historical Context: Why This Function Exists
This isn’t a new problem. Fortran had HYPOT in the 1970s for exactly this reason:
1970s: Fortran introduces HYPOT - Numerical stability was crucial for scientific computing - Engineers knew the overflow problem
1990s: C/C++ add hypot() to math.h - Standard library function - Same overflow protection
2015: JavaScript ES6 adds Math.hypot() - Same concept, JavaScript syntax - Extended to support n dimensions (not just 2)The algorithm has been battle-tested for decades. It’s not just convenience - it’s correctness.
Summary
I learned that Math.sqrt(x*x + y*y) is a landmine waiting to explode. When values are large, it overflows. When values are tiny, it underflows.
The fix is simple:
// Change this:const dist = Math.sqrt(x*x + y*y);
// To this:const dist = Math.hypot(x, y);Key points:
Math.hypot()calculates the square root of the sum of squares- It handles overflow and underflow automatically
- It accepts any number of arguments for n-dimensional distance
- It’s slightly slower than naive
sqrt(x*x + y*y)but correct - Use it anywhere you calculate distance, magnitude, or vector length
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
- 👨💻 MDN Web Docs: Math.hypot()
- 👨💻 Reddit: TIL about Math.hypot()
- 👨💻 ECMAScript Specification: Math.hypot
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments