Zaokrąglanie liczb w JavaScript wygląda banalnie, dopóki nie trafisz na wartości ujemne, ceny z groszami albo generowanie losowych indeksów. Math.floor() rozwiązuje dokładnie ten rodzaj problemów: sprowadza liczbę do największej liczby całkowitej mniejszej lub równej podanej wartości. W tym artykule pokazuję, jak działa w praktyce, kiedy wybrać ją zamiast innych metod i jak uniknąć błędów, które w kodzie pojawiają się zaskakująco często.
Najważniejsze zasady działania Math.floor()
- Zwraca liczbę całkowitą mniejszą lub równą wejściu, więc dla dodatnich wartości działa jak obcięcie części po przecinku.
- Dla liczb ujemnych nie ucina do zera - wynik przesuwa się w stronę bardziej ujemnej wartości.
-
Nie jest tym samym co
Math.round()aniMath.trunc(), a ta różnica ma znaczenie w kodzie produkcyjnym. - Świetnie sprawdza się przy losowaniu indeksów, dzieleniu na koszyki i mapowaniu wartości na siatkę.
- W obliczeniach finansowych lepiej uważać na liczby zmiennoprzecinkowe i pracować na najmniejszych jednostkach, np. groszach.
Czym jest Math.floor() i co zwraca
Math.floor() jest statyczną metodą obiektu Math, więc używa się jej zawsze w formie Math.floor(liczba). Zwracany wynik nadal ma typ number, ale bez części ułamkowej. W praktyce oznacza to, że dla liczb dodatnich zachowuje się jak obcięcie po przecinku, ale dla liczb ujemnych już nie: tu wynik przesuwa się w dół na osi liczbowej.
Math.floor(7.9); // 7
Math.floor(7.1); // 7
Math.floor(7); // 7
Math.floor(-7.1); // -8
Math.floor(-7.9); // -8To właśnie ta różnica sprawia, że ta funkcja bywa niedoceniana przez początkujących, a potem ratuje w miejscach, gdzie trzeba pilnować limitów, przedziałów albo indeksów tablic. Żeby zobaczyć, dlaczego ujemne wartości potrafią zaskoczyć, trzeba spojrzeć na sam kierunek zaokrąglania.
Jak zachowuje się dla liczb dodatnich i ujemnych
Najprościej myśleć o Math.floor() jak o ruchu w stronę minus nieskończoności. Dla dodatnich liczb efekt wygląda niewinnie, bo 4.9 staje się 4, ale przy wartościach ujemnych nie ma już „ucięcia do zera” - -4.1 staje się -5, a nie -4.
| Wartość wejściowa | Wynik | Co się dzieje |
|---|---|---|
| 4.9 | 4 | Funkcja usuwa część ułamkową i nie przekracza granicy. |
| -4.1 | -5 | Wynik idzie w dół, czyli dalej od zera. |
| -4.0 | -4 | Liczba całkowita pozostaje bez zmian. |
| -0.1 | -1 | To najczęstszy punkt zaskoczenia przy pierwszym kontakcie z floor. |
Ja lubię zapamiętywać jedną regułę: jeśli liczba jest dodatnia, floor() wygląda jak zwykłe obcięcie; jeśli jest ujemna, trzeba myśleć o osi liczbowej, a nie o samym przecinku. Gdy ta zasada jest już jasna, łatwiej dobrać właściwą metodę zaokrąglania do konkretnego zadania.
Kiedy wybrać floor, ceil, round albo trunc
W projektach nie chodzi o to, żeby znać nazwę funkcji, tylko żeby dobrać ją do celu. Ja najczęściej rozróżniam te cztery metody tak: jedna pilnuje dolnego limitu, druga górnego, trzecia wybiera najbliższą wartość, a czwarta po prostu odcina część ułamkową.
| Funkcja | Co robi | Kiedy użyć | Na co uważać |
|---|---|---|---|
Math.floor() |
Zaokrągla w dół do największej liczby całkowitej mniejszej lub równej wynikowi. | Gdy nie wolno przekroczyć limitu, np. przy indeksach, koszykach, przedziałach. | Przy liczbach ujemnych wynik może być bardziej ujemny, niż się spodziewasz. |
Math.ceil() |
Zaokrągla w górę do najmniejszej liczby całkowitej większej lub równej wynikowi. | Gdy chcesz domknąć brakujące miejsce, np. liczbę stron, paczek albo pudełek. | Nie używaj go tam, gdzie wynik ma pozostać poniżej limitu. |
Math.round() |
Zaokrągla do najbliższej liczby całkowitej. | Gdy zależy Ci na najbardziej naturalnym wyniku dla człowieka. | Przy liczbach ujemnych i dokładnie połówkowych warto sprawdzić zachowanie funkcji. |
Math.trunc() |
Odcina część ułamkową bez zmiany kierunku na osi liczbowej. | Gdy chcesz po prostu usunąć część dziesiętną, bez „schodzenia” niżej. | To nie jest to samo co floor() dla wartości ujemnych. |
Najważniejsza różnica praktyczna jest między floor() a trunc(): przy dodatnich liczbach wyglądają podobnie, ale przy ujemnych dają inny wynik. Jeśli liczysz coś „od zera” i nie chcesz, aby minus przewracał logikę, ten detal ma znaczenie. To właśnie dlatego warto zobaczyć kilka realnych zastosowań zamiast patrzeć na same definicje.
Najpraktyczniejsze zastosowania w JavaScript
Tu Math.floor() zaczyna się naprawdę opłacać. Najczęściej używam jej wtedy, gdy wynik ma trafić do całkowitego przedziału, a nie do „prawie całkowitej” wartości.
| Zastosowanie | Przykładowy wzór | Dlaczego to działa |
|---|---|---|
| Losowy indeks tablicy | Math.floor(Math.random() * arr.length) |
Math.random() zwraca wartość mniejszą od 1, więc wynik zawsze mieści się w zakresie 0 do length - 1. |
| Dzielenie na koszyki | Math.floor(score / 10) |
Wynik grupuje wartości w przedziały, np. 0-9, 10-19, 20-29. |
| Mapowanie na siatkę | Math.floor(x / cellSize) |
Pozycja pikselowa staje się numerem komórki albo kafelka. |
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}Ten wzór daje zakres min–max włącznie, bo Math.random() zwraca wartość mniejszą od 1, a Math.floor() zamienia ją na pełną liczbę całkowitą. Widzisz tu też ważną rzecz: do liczenia liczby stron w paginacji zwykle lepsze jest Math.ceil(), bo ostatnia niepełna strona też musi się pojawić. Jeśli potrzebujesz nie całych jednostek, tylko obcięcia do określonego miejsca po przecinku, dochodzi jeszcze jeden krok.
Jak zaokrąglać do części dziesiętnych bez psucia wyniku
Math.floor() działa na całej liczbie, ale czasem trzeba zejść tylko do dwóch miejsc po przecinku albo do jednego miejsca po przecinku. Wtedy stosuję prosty schemat: mnożenie, floor, dzielenie. To działa dobrze w wielu sytuacjach technicznych, na przykład przy progach cenowych, pomiarach czy wartościowaniu wyników.
const value = 12.349;
const floorTo2 = Math.floor(value * 100) / 100; // 12.34
const floorTo1 = Math.floor(value * 10) / 10; // 12.3Trzeba jednak pamiętać o jednym ograniczeniu: JavaScript używa liczb zmiennoprzecinkowych, więc przy precyzyjnych obliczeniach, zwłaszcza finansowych, mogą pojawić się minimalne odchylenia. W projektach związanych z pieniędzmi ja wolę trzymać kwoty w najmniejszych jednostkach, na przykład w groszach, zamiast ciągle mnożyć i dzielić liczby dziesiętne. Kiedy zaczynasz tak przeliczać wartości, pojawiają się jeszcze przypadki graniczne, które warto znać zawczasu.
Najczęstsze błędy i przypadki graniczne
Najwięcej problemów nie robi sama funkcja, tylko założenia, które projektujemy wokół niej. Warto zapamiętać kilka rzeczy, bo one wracają w code review zaskakująco często:
-
Math.floor(NaN)zwracaNaN, więc najpierw trzeba zadbać o poprawność wejścia. -
Infinityi-Infinityzostają bez zmian, bo nie są zwykłymi liczbami z częścią ułamkową. -
Math.floor(-0.1)daje-1, a nie 0. -
Math.floor()nie zaokrągla do najbliższej liczby, więc nie zastępujeMath.round(). - Przy pieniądzach lepiej myśleć w najmniejszych jednostkach, bo mnożenie i dzielenie liczb zmiennoprzecinkowych może wprowadzać drobne błędy.
Jeśli chcesz tylko obciąć część dziesiętną bez zmiany kierunku na osi liczbowej, wtedy sprawdzasz, czy nie lepszy będzie Math.trunc(). Z takim zestawem reguł łatwiej używać tej funkcji świadomie, zamiast zgadywać, co zrobi z danym wynikiem.
Co warto zapamiętać, zanim wstawisz floor do projektu
Ja zapamiętuję Math.floor() jako narzędzie do sytuacji, w których wynik nie może przekroczyć granicy: indeksów, bucketów, zakresów i prostych przeliczeń na całkowite jednostki. Jeśli masz liczbę dodatnią, funkcja zachowuje się intuicyjnie; jeśli wejście może być ujemne, trzeba myśleć o kierunku na osi liczbowej, a nie o samym „obcięciu”.
W praktyce to właśnie ten drobny detal odróżnia kod poprawny od kodu, który tylko wygląda poprawnie w testach na dodatnich liczbach. Gdy to rozróżnienie masz już opanowane, dobór między floor(), ceil(), round() i trunc() przestaje być zgadywaniem, a staje się prostą decyzją techniczną.