Obiekt `Math`

Ten artykuł wyjaśnia obiekt Math.

Wyjaśnia krok po kroku, od podstawowego użycia przez typowe praktyczne wzorce, aż po pułapki i sposoby radzenia sobie z nimi.

YouTube Video

Obiekt Math

Obiekt Math w JavaScripcie udostępnia zestaw wbudowanych narzędzi do obliczeń numerycznych.

Stałe

Stałe zdefiniowane w Math są przydatne przy pracy z wartościami o wysokiej precyzji.

Poniższy kod pokazuje niektóre typowe stałe.

1// Show common Math constants
2console.log("Math.PI:", Math.PI);         // 3.141592653589793
3console.log("Math.E:", Math.E);           // Euler's number
4console.log("Math.LN2:", Math.LN2);       // Natural log of 2
5console.log("Math.SQRT2:", Math.SQRT2);   // Square root of 2
  • Te stałe można używać bezpośrednio do funkcji trygonometrycznych, przekształceń logarytmicznych, normalizacji i innych zastosowań. Na przykład Math.PI jest potrzebne do przeliczania kątów na radiany.

Podstawowe zaokrąglanie i wartości bezwzględne (abs, floor, ceil, round, trunc)

Zaokrąglanie i obsługa znaków są często używane, dlatego ważne jest, aby dobrze rozumieć różnice między Math.floor, Math.ceil i Math.round. Zwłaszcza przy pracy z liczbami ujemnymi wyniki mogą być nieintuicyjne, dlatego trzeba uważać, jak działa każde zaokrąglenie i stosować je odpowiednio.

Poniższe przykłady ilustrują różnice między funkcjami zaokrąglającymi.

 1// Examples of rounding and absolute value functions
 2const pos = 3.7;
 3const neg = -3.7;
 4
 5// --- floor ---
 6console.log("Math.floor(3.7):", Math.floor(pos));  // 3
 7console.log("Math.floor(-3.7):", Math.floor(neg)); // -4
 8// (rounds toward smaller value)
 9
10// --- ceil ---
11console.log("Math.ceil(3.7):", Math.ceil(pos));  // 4
12console.log("Math.ceil(-3.7):", Math.ceil(neg)); // -3
13// (rounds toward larger value)
14
15// --- round ---
16console.log("Math.round(3.5):", Math.round(3.5));   // 4
17// (.5 goes up for positive numbers)
18console.log("Math.round(-3.5):", Math.round(-3.5)); // -3
19// (.5 moves toward zero for negative numbers)
20
21// --- trunc ---
22console.log("Math.trunc(3.7):", Math.trunc(pos));  // 3
23console.log("Math.trunc(-3.7):", Math.trunc(neg)); // -3
24// (fraction removed toward zero)
25
26// --- abs ---
27console.log("Math.abs(-3.7):", Math.abs(neg)); // 3.7
  • Math.floor zaokrągla w dół dla liczb ujemnych, więc -3.7 staje się -4.
  • Math.ceil zaokrągla w górę dla liczb ujemnych, więc -3.7 staje się -3.
  • Math.round zaokrągla .5 w górę dla liczb dodatnich, a dla ujemnych zaokrągla w stronę zera.
  • Liczby ujemne mogą generować wyniki inne niż intuicyjne, dlatego ważne jest jasne zrozumienie kierunku zaokrąglania.
  • Funkcje zaokrąglania należy wybierać w zależności od zastosowania. Na przykład Math.floor używaj do obliczania indeksów, Math.ceil do wyznaczania górnych granic, a Math.trunc, gdy chcesz po prostu usunąć część dziesiętną.

Mnożenie, potęgowanie i pierwiastki (pow, **, sqrt, cbrt, hypot)

Potęgowanie i pierwiastkowanie można wykonać za pomocą Math.pow lub operatora **. Math.hypot bezpiecznie oblicza pierwiastek kwadratowy z sumy kwadratów (odległość).

Poniżej znajdują się przykłady potęgowania oraz funkcji hypot. Math.hypot zmniejsza ryzyko przepełnienia i niedomiaru.

1// Power, root and hypot examples
2console.log("2 ** 10:", 2 ** 10);                // 1024
3console.log("Math.pow(2, 10):", Math.pow(2, 10));// 1024
4console.log("Math.sqrt(16):", Math.sqrt(16));    // 4
5console.log("Math.cbrt(27):", Math.cbrt(27));    // 3
6console.log("Math.hypot(3, 4):", Math.hypot(3, 4));// 5 (3-4-5 triangle)
  • Math.hypot jest przydatne przy pracy z bokami trójkąta lub długościami wektorów wielowymiarowych.

Funkcje wykładnicze i logarytmiczne (exp, log, log10, log2)

Funkcje wykładnicze i logarytmiczne są często używane w rachunku prawdopodobieństwa, rozpadzie wykładniczym oraz skalowaniu.

Poniżej podstawowe przykłady użycia exp i logarytmów. Upewnij się, że używasz odpowiedniej funkcji w zależności od podstawy logarytmu.

1// Exponential and logarithm examples
2console.log("Math.exp(1):", Math.exp(1));     // e^1
3console.log("Math.log(Math.E):", Math.log(Math.E)); // natural log, 1
4console.log("Math.log10(100):", Math.log10(100));   // 2
5console.log("Math.log2(8):", Math.log2(8));         // 3
  • Logarytmy naturalne (Math.log) są standardem w statystyce i równaniach różniczkowych. Logarytmy dziesiętne i o podstawie 2 są również wybierane zależnie od zastosowania.

Funkcje trygonometryczne (sin, cos, tan, asin, acos, atan2)

Funkcje trygonometryczne są niezbędne do obliczeń kątów, obrotów i przekształceń współrzędnych. Math.atan2(y, x) jest przydatne do obliczania kątów z uwzględnieniem odpowiednich ćwiartek płaszczyzny.

Poniżej przedstawiono podstawowe przykłady i zastosowania atan2. Kąty muszą być obsługiwane w radianach.

 1// Trigonometry examples (angles in radians)
 2const degToRad = deg => deg * (Math.PI / 180);
 3const radToDeg = rad => rad * (180 / Math.PI);
 4
 5console.log("Math.sin(PI/2):", Math.sin(Math.PI / 2));          // 1
 6console.log("Math.cos(0):", Math.cos(0));                      // 1
 7console.log("Math.tan(Math.PI / 4):", Math.tan(Math.PI / 4));  // ~1
 8
 9// Using atan2 to compute angle of vector (x, y)
10const x = -1, y = 1;
11const angle = Math.atan2(y, x); // returns angle in radians taking quadrant into account
12console.log("atan2(1, -1) in degrees:", radToDeg(angle));      // 135
  • atan2 umożliwia bezpieczne określenie kąta wektora, co jest przydatne przy obrotach i obliczaniu kierunku.

Generowanie liczb losowych (Math.random i praktyczne wzorce)

Math.random() zwraca losową liczbę z rozkładu jednostajnego w przedziale [0, 1). W razie potrzeby można przekształcić tę wartość na liczby całkowite lub rozkład normalny.

Poniżej znajdują się przykłady funkcji pomocniczych do generowania liczb losowych. Jeśli potrzebujesz liczb losowych bezpiecznych kryptograficznie, użyj crypto.getRandomValues.

 1// Random utilities using Math.random
 2
 3// Get integer in [min, max] inclusive
 4function randomInt(min, max) {
 5  // Returns integer between min and max (inclusive)
 6  return Math.floor(Math.random() * (max - min + 1)) + min;
 7}
 8console.log("randomInt(1, 6):", randomInt(1, 6)); // dice roll example
 9
10// Get float in [min, max)
11function randomFloat(min, max) {
12  return Math.random() * (max - min) + min;
13}
14console.log("randomFloat(0, 5):", randomFloat(0, 5));
15
16// Fisher-Yates shuffle
17function shuffle(array) {
18  for (let i = array.length - 1; i > 0; i--) {
19    const j = Math.floor(Math.random() * (i + 1));
20    // swap array[i] and array[j]
21    [array[i], array[j]] = [array[j], array[i]];
22  }
23  return array;
24}
25console.log("shuffle([1,2,3,4,5]):", shuffle([1,2,3,4,5]));
  • Math.random jest wygodne do ogólnych zastosowań, ale w przypadku powtórek w grach lub kryptografii konieczne są dodatkowe środki zapewniające powtarzalność lub bezpieczeństwo.

Praktyczne pomocniki (clamp, lerp, mapRange, normalizacja kątów)

Warto napisać własne małe funkcje pomocnicze do często wykonywanych zadań matematycznych.

Oto przykłady implementacji często używanych funkcji pomocniczych.

 1// Utility functions: clamp, lerp, mapRange, normalizeAngle
 2
 3// Clamp value between min and max
 4function clamp(v, min, max) {
 5  return Math.max(min, Math.min(max, v));
 6}
 7console.log("clamp(10, 0, 5):", clamp(10, 0, 5)); // 5
 8
 9// Linear interpolation: t in [0,1]
10function lerp(a, b, t) {
11  return a + (b - a) * t;
12}
13console.log("lerp(0, 10, 0.25):", lerp(0, 10, 0.25)); // 2.5
14
15// Map value from one range to another
16function mapRange(v, inMin, inMax, outMin, outMax) {
17  const t = (v - inMin) / (inMax - inMin);
18  return lerp(outMin, outMax, t);
19}
20console.log("mapRange(5, 0, 10, 0, 100):", mapRange(5, 0, 10, 0, 100));
21// -> 50
22
23// Normalize angle to [-PI, PI)
24function normalizeAngle(rad) {
25  return Math.atan2(Math.sin(rad), Math.cos(rad));
26}
27console.log("normalizeAngle(3*PI):", normalizeAngle(3 * Math.PI));
  • Ten kod zawiera małe funkcje pomocnicze powszechnie używane do operacji numerycznych, takich jak ograniczanie wartości (clamp), interpolacja, mapowanie zakresów czy normalizacja kątów. clamp ogranicza wartość do określonego zakresu, natomiast lerp i mapRange wykonują interpolację lub konwersję do innego zakresu. Ponadto normalizeAngle normalizuje kąty do zakresu [-π, π), aby stabilizować obliczenia dotyczące obrotów.
  • Te funkcje pomocnicze są często wykorzystywane w grafice, grach i programowaniu interakcji. normalizeAngle jest ważna przy porównywaniu różnic kątowych i interpolacji.

Pułapki arytmetyki zmiennoprzecinkowej i zaokrąglania (precyzja i porównywanie)

Liczby w JavaScripcie to liczby zmiennoprzecinkowe podwójnej precyzji zgodne z IEEE-754 (64-bitowe), dlatego należy zachować ostrożność przy porównywaniu i zaokrąglaniu. Na przykład: 0.1 + 0.2 !== 0.3.

Poniżej przykład obsługi błędów zaokrąglania. Ważne jest stosowanie porównań tolerujących drobne błędy lub odpowiednich funkcji zaokrąglających.

1// Floating point precision example and safe equality
2console.log("0.1 + 0.2 === 0.3 :", 0.1 + 0.2 === 0.3); // false
3
4function nearlyEqual(a, b, eps = 1e-12) {
5  return Math.abs(a - b) <= eps;
6}
7console.log("nearlyEqual(0.1+0.2, 0.3):", nearlyEqual(0.1 + 0.2, 0.3));
8// -> true
  • Aby niezawodnie sprawdzać równość liczb, używaj błędu bezwzględnego (eps) lub względnego.

Wskazówki dotyczące wydajności

Funkcje obiektu Math są natywnie zoptymalizowane i często działają szybciej niż własnoręcznie napisane odpowiedniki. Jeśli to samo obliczenie (np. Math.PI / 180) jest wykonywane w pętli, przypisanie go wcześniej do zmiennej zmniejsza niepotrzebne koszty.

Poniżej przykład przeliczania stopni na radiany za pomocą stałej utworzonej poza pętlą.

1// Cache conversion factor for performance
2const DEG_TO_RAD = Math.PI / 180;
3const RAD_TO_DEG = 180 / Math.PI;
4
5for (let deg = 0; deg < 360; deg += 10) {
6  // Use cached constant instead of computing each time
7  const rad = deg * DEG_TO_RAD;
8  // ... do work with rad
9}
  • Najlepiej jest sprawdzić rzeczywiste wąskie gardła za pomocą profilera przed przeprowadzeniem optymalizacji.
  • Warto buforować często używane stałe.

Zgodność i polyfill’e

Obiekt Math istnieje od dawna, ale niektóre funkcje, takie jak Math.cbrt, Math.log10, Math.log2 i Math.hypot mogą nie być obsługiwane w starszych środowiskach. Przygotuj prosty polyfill w razie potrzeby.

Poniżej znajduje się przykład prostego polyfill’a dla Math.log2.

1// Polyfill for Math.log2 if needed
2if (!Math.log2) {
3  Math.log2 = function(x) {
4    return Math.log(x) / Math.LN2;
5  };
6}
7console.log("Math.log2(8):", Math.log2(8));

Wiele środowisk już je obsługuje, lecz w razie potrzeby sprawdź zgodność.

Sprawdź wsparcie przeglądarek zgodnie z docelowym środowiskiem projektu.

Przykład praktyczny: aktualizacje krokowe w prostej symulacji fizycznej

Na koniec praktyczny przykład łączący kilka funkcji Math. To bardzo prosty przykład do aktualizacji pozycji, prędkości i sterowania kątem.

To bardzo uproszczony model silnika fizycznego.

 1// Clamp value between min and max
 2function clamp(v, min, max) {
 3  return Math.max(min, Math.min(max, v));
 4}
 5
 6// Normalize angle to [-PI, PI)
 7function normalizeAngle(rad) {
 8  return Math.atan2(Math.sin(rad), Math.cos(rad));
 9}
10
11// Simple physics step example using Math utilities
12function step(state, dt) {
13  // state: { x, y, vx, vy, angle, angularVelocity }
14  // dt: time delta in seconds
15  // Integrate linear motion
16  state.x += state.vx * dt;
17  state.y += state.vy * dt;
18
19  // Integrate angular motion and normalize angle
20  state.angle = normalizeAngle(state.angle + state.angularVelocity * dt);
21
22  // Keep position within bounds [0, width] x [0, height]
23  state.x = clamp(state.x, 0, state.width);
24  state.y = clamp(state.y, 0, state.height);
25  return state;
26}
27
28const state = { x: 10, y: 10, vx: 2, vy: 0.5, angle: 0, angularVelocity: 0.1, width: 100, height: 100 };
29console.log("before:", state);
30step(state, 0.016); // simulate ~1 frame at 60fps
31console.log("after:", state);
  • Łącząc w ten sposób pomocnicze funkcje Math, logikę symulacji i animacji można uprościć.

Podsumowanie

  • Math obejmuje wszystko — od stałych i podstawowych funkcji do tych zaawansowanych, będąc fundamentem przetwarzania numerycznego.
  • Wybieraj funkcje zgodnie z ich przeznaczeniem — do liczb losowych, trygonometrii, wykładników i logarytmów czy zaokrągleń — oraz twórz własne proste pomocniki, jak clamp i lerp, do praktycznych zastosowań.
  • Zwracaj uwagę na precyzję zmiennoprzecinkową i wsparcie funkcji w docelowym środowisku, a w razie potrzeby stosuj tolerancję błędu lub polyfill’e.

Możesz śledzić ten artykuł, korzystając z Visual Studio Code na naszym kanale YouTube. Proszę również sprawdzić nasz kanał YouTube.

YouTube Video