最近我在做數據可視化和圖形圖像渲染相關的技術研究,而圖形圖像涉及到許多幾何圖形的向量運算,我們求一個向量的模,需要計算每個座標分量的平方和,然後開根號。
一般來説,我們使用類似如下的代碼:
function length(v) {
const dimension = v.length;
let ret = 0;
for(let i = 0; i < dimension; i++) {
ret += v[i] ** 2;
}
return Math.sqrt(ret);
}
const v1 = [3, 4];
console.log(length(v1)); // 5
複製代碼
但是,在 ES2015 中,提供了Math.hypot
方法,來計算參數的平方根。
const v1 = [3, 4];
console.log(Math.hypot(...v1)); // 5
複製代碼
目前除了 IE 外,其他的瀏覽器都支持這個方法。
👉🏻 冷知識 —— Math.hypot 的坑:你可能會認為 Math.hypot 是內置函數,它的性能應該要比用 Math.sqrt 好,但是實際上並不是這樣。我們寫一段測試腳本:
function length(v) {
const dimension = v.length;
let ret = 0;
for(let i = 0; i < dimension; i++) {
ret += v[i] ** 2;
}
return Math.sqrt(ret);
}
複製代碼
// Math.hypot
const v1 = Array(10).fill(0).map(() => Math.random());
const result = Math.hypot(...v1);
複製代碼
//Math.sqrt
const v1 = Array(10).fill(0).map(() => Math.random());
const result = length(v1);
複製代碼
用 jsperf 進行測試,結果令人驚訝,在我的 Chrome 75 下,使用 Math.hypot 竟然比使用 Math.sqrt 慢 40% 左右。
也許是我使用的姿勢不對?但是 kangax 大神早在 14 年的時候就寫過更多 case 來測試:
結果。。。
所以一個 ES2015 開始支持的數學函數,又是很常用的運算,卻沒有多少人使用,也沒有多少人介紹它,也是有理由的 —— 性能捉急。
不過 hypot 性能不如 sqrt,也並不意味着它完全不可用。
在某些情況下,使用 hypot 能得到 sqrt 無法得到的結果。比如特別大的數或者特別小的數值下:
Math.hypot(2e200, 3e200); // 3.6055512754639894e+200
Math.sqrt(2e200 ** 2 + 3e200 ** 2); // Infinity
Math.hypot(2e-200, 3e-200); // 3.6055512754639893e-200
Math.sqrt(2e-200 ** 2 + 3e-200 ** 2); // 0
複製代碼
所以,如果我們要處理很大的數或很小的數時,可以使用 Math.hypot,在一般情況下,還是使用 Math.sqrt 為好。例如:
const result = Math.sqrt(x ** 2 + y ** 2);
if(!Number.isFinite(result)) result = Math.hypot(x, y);
複製代碼
關於 Math.hypot 還有什麼問題,可以在 issue 中討論。