Blog webdeveloperski Patryk yarpo Jar

[JS] Zaokrąglanie liczb z zadaną dokładnością

Autor wiadomości Marzec 6, 2011

Jeśli chcemy w JS sprowadzić liczbę z częścią ułamkową do liczby całkowitej możemy wykorzystać metodę Math.round(liczba).

Metoda ta przyjmuje jeden parametr, jest to liczba, która zostanie zaokrąglona wg prawideł matematycznych. Co jednak kiedy chcemy uzyskać zadaną liczbę miejsc po przecinku?

Nie ma wprost metody zaokraglanie(liczba, doMiejscPoPrzecinku). Można jednak łatwo napisać funkcję, która to dla nas zrobi:

function Round(n, k)
{
    var factor = Math.pow(10, k);
    return Math.round(n*factor)/factor;
}
var example = 12.345453534534;
alert(Round(example, 2));
alert(Round(example, 1));
alert(Round(example, 0));
alert(Round(example, 15));

W wyniku otrzymamy kolejno: 12.34, 12.3, 12.3, 12, 12.345453534534.

Ulepszenia

Powyższy kod działa. Jednak ma kilka wad:

  1. w rzeczywistości nie zaokrągla, a jedynie ucina w odpowiednim miejscu liczbę - dla pierwszego przykładu powinien zwrócić 12.35
  2. nie jest "podpięty" pod żaden obiekt, a więc łatwo może zostać nadpisany (w dużym projekcie, jeśli nie zaczniesz dzielić kodu na moduły na pewno gdzieś ktoś wpadnie na pomysł by nazwać jakąś funkcję tak samo - wJS nie dostaniesz żadnego komunikatu - ot, kod będzie jedynie źle działał).

Łata #1

function Round(n, k) 
{
    var factor = Math.pow(10, k+1);
    n = Math.round(Math.round(n*factor)/10);
    return n/(factor/10);
}
alert(Round(12.345453534534, 2));
alert(Round(12.344, 2));
alert(Round(12.5, 0));
alert(Round(12.345453534534, 15));

Wynik: 12.35, 12.34, 13, 12.345453534534

Łata #2

Skorzystamy tu z rozszerzania istniejących obiektów.

Math.decimal = function(n, k) 
{
    var factor = Math.pow(10, k+1);
    n = Math.round(Math.round(n*factor)/10);
    return n/(factor/10);
}
alert(Math.decimal(12.345453534534, 2));
alert(Math.decimal(12.344, 2));
alert(Math.decimal(12.5, 0));
alert(Math.decimal(12.345453534534, 15));

W ten sposób otrzymujemy całkiem solidany kawałek kodu. Nie waliduje się tu w żaden sposób danych. Często to niedopuszczalne. Pamiętaj jednak o zasadzie YANGNI ("You Are Not Gonna Need It"). Nie koduj na zapas, chyba że dla praktyki 🙂 [ten artykuł miał pokazać zasadę, a nie dostarczać gotowca piorącego, sprzątającego i zmywającego po obiedzie].

Komentarze (8) Trackbacks (0)
  1. +(34.12345678.toFixed(2)) –> 34.12
    +(34.56999.toFixed(2)) –> 34.57

    Wolę tak 😛 Ale ciekawe co jest szybsze.

  2. var moja = new Date().getTime();
    Math.decimal = function(n, k)
    {
    var factor = Math.pow(10, k+1);
    n = Math.round(Math.round(n*factor)/10);
    return n/(factor/10);
    }
    for(var i = 0; i < 1000; i++) { Math.decimal(12.345453534534, 2); Math.decimal(12.344, 2); } console.log((new Date().getTime()) - moja); var twoja = new Date().getTime(); for(var i = 0; i < 1000; i++) { +(12.345453534534.toFixed(2)); +(12.344.toFixed(2)); } console.log((new Date().getTime()) - twoja); Wg moich testów, twój sposob jest ok. 2x szybszy.

  3. Przyznaję, że rzeczywiście toFixed jest bardzo wygodny. Choć ja osobiście preferuję taki zapis:

    Math.decimal(12.345453534534, 2);

    wydaje mi się on bardziej samokomentujący. Choć jego bebechy z pewnościa mogłyby być wydajniejsze 🙂

  4. 378.695.toFixed(2) -> 378.69 (źle)
    za to
    378.625.toFixed(2) -> 378.30 (dobrze)
    Błąd JS?

  5. Ciekawy przykład 🙂
    W takim wypadku moja funkcja działa poprawnie :). Choć może to ma jakies uzasadnienie. trzeba by zajrzeć w jakąś dokumentację toFixed

  6. Math.decimal(14.475 , 0) zwróci 15, a powinno byc 14 🙂

  7. Rzeczywiście 🙂

    Ciekawy przypadek testowy. Sądzę, że można to dosyć łatwo załatać:
    Math.decimal = function(n, k)
    {
    if (k < 1) { return Math.round(n); } var factor = Math.pow(10, k+1); n = Math.round(Math.round(n*factor)/10); return n/(factor/10); } W tej sytuacji kod staje się odporny na takie przypadki. Dzięki za znalezienie błędu 🙂

  8. a nie lepiej najpierw pomnożyć liczbę zaokrąglaną do takiej potęgi 10 do ilu miejsc chcemy zaokrąglić a potem podzielić otrzymany wynik przez tą liczbę ?


Leave a comment

 

Brak trackbacków.