Pytanie Zaokrąglanie do co najwyżej 2 miejsc po przecinku (tylko w razie potrzeby)


Chciałbym zaokrąglić maksymalnie 2 miejsca po przecinku, ale tylko w razie potrzeby.

Wkład:

10
1.7777777
9.1

Wydajność:

10
1.78
9.1

Jak mogę to zrobić? JavaScript?


1845
2017-08-06 17:17


pochodzenie


Zrobiłem skrzypce z wieloma technikami oferowanymi tutaj jako rozwiązania ... dzięki czemu możesz porównać: skrzypce - dsdsdsdsd
Nikt nie wydaje się być tego świadomy Number.EPSILON. Posługiwać się Math.round( num * 100 + Number.EPSILON ) / 100. - cronvel
W przypadku nowych czytelników nie możesz tego zrobić chyba że masz wynik typu łańcucha. Obliczenia matematyczne zmiennoprzecinkowe na binarnych wewnętrznych reprezentacjach liczb oznaczają, że są zawsze liczby, które nie mogą być reprezentowane jako czyste dziesiętne. - Walf


Odpowiedzi:


Posługiwać się Math.round(num * 100) / 100


2292
2017-08-06 17:20



Chociaż będzie to działało w większości przypadków, nie będzie działać dla 1.005, które zakończy się wyniesieniem 1 zamiast 1.01 - James
@James Wow to naprawdę dziwne - pracuję na konsoli przeglądarki Chrome i zauważam, że 1.005 * 100 = 100.49999999999999. Math.round (100.49999999999999) ocenia na 100, podczas gdy Math.round (100.5) ocenia na 101. IE9 robi to samo. Jest to spowodowane zmienność punktowa w javascript - stinkycheeseman
Proste obejście. Dla 2 d.p. użyć Math.round((num + 0.00001) * 100) / 100. Próbować Math.round((1.005 + 0.00001) * 100) / 100 i Math.round((1.0049 + 0.00001) * 100) / 100 - mrkschan
@mrkschan Dlaczego to działa i czy jest to niezawodny dla wszystkich liczb? - CMCDragonkai
Dla tych, którzy tego nie rozumieją, ta technika nazywa się skalowaniem. Zasadniczo, odpowiedź brzmi: umieść dwie cyfry po przecinku, zamieniając liczbę w liczbę całkowitą, aby uniknąć wszystkich szalonych zagadek zmiennoprzecinkowych, zaokrąglając to, a następnie przetłumaczysz z powrotem na to, co było wcześniej, dzieląc przez 100, a otrzymasz odpowiedź na 2dp. - Alex_Nabu


Jeśli wartością jest typ tekstu:

parseFloat("123.456").toFixed(2);

Jeśli wartość jest liczbą:

var numb = 123.23454;
numb = numb.toFixed(2);

Wadą jest to, że wartości takie jak 1,5 dadzą "1.50" jako wynik. Poprawka zaproponowana przez @minitech:

var numb = 1.5;
numb = +numb.toFixed(2);
// Note the plus sign that drops any "extra" zeroes at the end.
// It changes the result (which is a string) into a number again (think "0 + foo"),
// which means that it uses only as many digits as necessary.

To wygląda jak Math.round jest lepszym rozwiązaniem. Ale to nie jest! W niektórych przypadkach będzie NIE okrągłe poprawnie:

Math.round(1.005 * 1000)/1000 // Returns 1 instead of expected 1.01!

toFixed () również będzie NIE zaokrąglić poprawnie w niektórych przypadkach (testowane w Chrome v.55.0.2883.87)!

Przykłady:

parseFloat("1.555").toFixed(2); // Returns 1.55 instead of 1.56.
parseFloat("1.5550").toFixed(2); // Returns 1.55 instead of 1.56.
// However, it will return correct result if you round 1.5551.
parseFloat("1.5551").toFixed(2); // Returns 1.56 as expected.

1.3555.toFixed(3) // Returns 1.355 instead of expected 1.356.
// However, it will return correct result if you round 1.35551.
1.35551.toFixed(2); // Returns 1.36 as expected.

Chyba to dlatego, że 1.555 jest w rzeczywistości czymś podobnym do float 1.55499994 za kulisami.

Rozwiązanie 1 jest użycie skryptu z wymaganym algorytmem zaokrąglania, na przykład:

function roundNumber(num, scale) {
  if(!("" + num).includes("e")) {
    return +(Math.round(num + "e+" + scale)  + "e-" + scale);
  } else {
    var arr = ("" + num).split("e");
    var sig = ""
    if(+arr[1] + scale > 0) {
      sig = "+";
    }
    return +(Math.round(+arr[0] + "e" + sig + (+arr[1] + scale)) + "e-" + scale);
  }
}

https://plnkr.co/edit/uau8BlS1cqbvWPCHJeOy?p=preview

Rozwiązanie 2 jest unikanie obliczeń front end i pobieranie zaokrąglonych wartości z serwera zaplecza.


2327
2017-10-11 00:27



To jedno (ToFixed) podejście jest dobre i działa dla mnie, ale tak właśnie robi nie zastosuj się do pierwotnego żądania "tylko wtedy, gdy jest to konieczne". (Zaokrągla 1.5 do 1.50, co łamie specyfikację.) - Per Lundberg
W przypadku wymagania "w razie potrzeby" wykonaj następujące czynności: parseFloat(number.toFixed(decimalPlaces));   @PerLundberg - Onur Yıldırım
parseFloat("55.555").toFixed(2) zwraca "55.55" w konsoli deweloperów Chrome. - Levi Botelho
Nie ma żadnej korzyści z używania opcji FIXed zamiast Math.round; toFixed prowadzi do dość podobnych problemów z zaokrąglaniem (spróbuj z 5.555 i 1.005), ale jest jak 500x (bez żartów) wolniejsze niż Math.round ... Wygląda na to, że odpowiedź @MarkG jest dokładniejsza tutaj. - Pierre
Dlaczego js jest taki dziwny? - D.Deriso


Możesz użyć

function roundToTwo(num) {    
    return +(Math.round(num + "e+2")  + "e-2");
}

Znalazłem to MDN. Ich sposób pozwala uniknąć problemu z 1.005 wzmiankowany.

roundToTwo(1.005)
1.01
roundToTwo(10)
10
roundToTwo(1.7777777)
1.78
roundToTwo(9.1)
9.1
roundToTwo(1234.5678)
1234.57

324
2017-08-21 12:56



@Redsandro, +(val) jest odpowiednikiem przymusu używania Number(val). Łączenie "e-2" z liczbą skutkowało ciągiem, który musiał zostać przekonwertowany z powrotem na liczbę. - Jack
Pamiętaj, że w przypadku dużych i malutkich pływaków, które wytworzyłyby NaN, np. + "1e-21 + 2", nie będzie poprawnie analizowany. - Pierre
"Rozwiązałeś" problem 1.005 ", ale wprowadziłeś nowy: teraz, w konsoli Chrome, roundToTwo(1.0049999999999999) wychodzi jako 1.01 (nieuchronnie, ponieważ 1.0049999999999999 == 1.005). Wydaje mi się, że float dostajesz, jeśli wpiszesz num = 1.005 "oczywiście" powinno "zaokrąglić do 1.00, ponieważ dokładna wartość num jest mniejsza niż 1,005. Oczywiście wydaje mi się również, że ciąg "1,005" oczywiście "powinien" być zaokrąglony do 1,01. Fakt, że różne osoby wydają się mieć różne intuicje co do tego, co właściwie działa, jest częścią tego, dlaczego jest to skomplikowane. - Mark Amery
Nie ma między nimi żadnej liczby zmiennoprzecinkowej 1.0049999999999999 i 1.005, więc z definicji są tej samej liczby. Nazywa się to cięciem dedekind. - Azmisov
@Zzmisow ma rację. Podczas 1.00499 < 1.005 jest true, 1.0049999999999999 < 1.005 ocenia na false. - falconepl


Odpowiedź MarkG jest poprawna. Oto ogólne rozszerzenie dla dowolnej liczby miejsc dziesiętnych.

Number.prototype.round = function(places) {
  return +(Math.round(this + "e+" + places)  + "e-" + places);
}

Stosowanie:

var n = 1.7777;    
n.round(2); // 1.78

Test jednostkowy:

it.only('should round floats to 2 places', function() {

  var cases = [
    { n: 10,      e: 10,    p:2 },
    { n: 1.7777,  e: 1.78,  p:2 },
    { n: 1.005,   e: 1.01,  p:2 },
    { n: 1.005,   e: 1,     p:0 },
    { n: 1.77777, e: 1.8,   p:1 }
  ]

  cases.forEach(function(testCase) {
    var r = testCase.n.round(testCase.p);
    assert.equal(r, testCase.e, 'didn\'t get right number');
  });
})

115
2017-11-01 07:40



Pierre podniósł poważny problem z odpowiedzią Markga. - dsjoerg
Uwaga: Jeśli nie chcesz zmieniać Number.prototype - po prostu zapisz to jako funkcję: function round(number, decimals) { return +(Math.round(number + "e+" + decimals) + "e-" + decimals); } - Philipp Tsipman
ja stworzyłem Math.roundPlaces(num, places), aby działał również na łańcuchach (na wszelki wypadek). - bradlis7
Spróbuj n: 1e + 19 - zwraca NaN - DavidJ
Algorytm ten zawsze zaokrągla się (zamiast oddalać od zera). Tak więc, w przypadku liczb ujemnych, wynik nie jest taki, jakiego można się spodziewać: (-1.005).round(2) === -1 - Aleksej Komarov


Można użyć .toFixed(NumberOfDecimalPlaces).

var str = 10.234.toFixed(2); // => '10.23'
var number = Number(str); // => 10.23

71
2017-10-21 17:02



Dlaczego nie jest to najlepsza odpowiedź? (EDYTOWAĆ: o, bo to konwertuje na ciąg :) - Daniel
Również dlatego, że dodaje końcowe zera, które jest nie to, o co pytano pierwotnie. - Alastair Maw
Ale kończące się zera można łatwo usunąć za pomocą wyrażenia regularnego, np. `Number (10.10000.toFixed (2) .replace (/ 0 + $ /, ''))` => 10.1 - Chad McElligott
@ Daniel the najlepsza odpowiedź jest taki sam (jak na razie), ale tak nie jest alsways okrągłe poprawnie, spróbuj +(1.005).toFixed(2) który wraca 1 zamiast 1.01. - Emile Bergeron
@ChadMcElligott: Twój regex nie działa dobrze z liczbami całkowitymi: Number(9).toFixed(2).replace(/0+$/, '') => "9." - Jacob van Lingen


Dokładna metoda zaokrąglania. Źródło: Mozilla

(function(){

    /**
     * Decimal adjustment of a number.
     *
     * @param   {String}    type    The type of adjustment.
     * @param   {Number}    value   The number.
     * @param   {Integer}   exp     The exponent (the 10 logarithm of the adjustment base).
     * @returns {Number}            The adjusted value.
     */
    function decimalAdjust(type, value, exp) {
        // If the exp is undefined or zero...
        if (typeof exp === 'undefined' || +exp === 0) {
            return Math[type](value);
        }
        value = +value;
        exp = +exp;
        // If the value is not a number or the exp is not an integer...
        if (isNaN(value) || !(typeof exp === 'number' && exp % 1 === 0)) {
            return NaN;
        }
        // Shift
        value = value.toString().split('e');
        value = Math[type](+(value[0] + 'e' + (value[1] ? (+value[1] - exp) : -exp)));
        // Shift back
        value = value.toString().split('e');
        return +(value[0] + 'e' + (value[1] ? (+value[1] + exp) : exp));
    }

    // Decimal round
    if (!Math.round10) {
        Math.round10 = function(value, exp) {
            return decimalAdjust('round', value, exp);
        };
    }
    // Decimal floor
    if (!Math.floor10) {
        Math.floor10 = function(value, exp) {
            return decimalAdjust('floor', value, exp);
        };
    }
    // Decimal ceil
    if (!Math.ceil10) {
        Math.ceil10 = function(value, exp) {
            return decimalAdjust('ceil', value, exp);
        };
    }
})();

Przykłady:

// Round
Math.round10(55.55, -1); // 55.6
Math.round10(55.549, -1); // 55.5
Math.round10(55, 1); // 60
Math.round10(54.9, 1); // 50
Math.round10(-55.55, -1); // -55.5
Math.round10(-55.551, -1); // -55.6
Math.round10(-55, 1); // -50
Math.round10(-55.1, 1); // -60
Math.round10(1.005, -2); // 1.01 -- compare this with Math.round(1.005*100)/100 above
// Floor
Math.floor10(55.59, -1); // 55.5
Math.floor10(59, 1); // 50
Math.floor10(-55.51, -1); // -55.6
Math.floor10(-51, 1); // -60
// Ceil
Math.ceil10(55.51, -1); // 55.6
Math.ceil10(51, 1); // 60
Math.ceil10(-55.59, -1); // -55.5
Math.ceil10(-59, 1); // -50

54
2017-08-01 08:02



Ktoś umieścił to na GitHub i npm: github.com/jhohlfeld/round10 - Jo Liss
Fajny i działający kod. dziękuję @Error. !! - Anahit DEV
Nie, Math.round10(3544.5249, -2) zwraca 3544,52 zamiast 3544,53 - Matija
@Matija Wolfram alpha mówi także 354,52. Chcesz zminimalizować błąd między bieżącą liczbą a zaokrągloną aproksymacją. Najbliższe przybliżenie 3544.5249 do 2 miejsc po przecinku to 3544.52 (błąd = 0,0049). Jeśli było 3544.53, błąd będzie 0,0051. Robisz kolejne zaokrąglanie, to znaczy Math.round10 (Math.round10 (3544.5249, -3), -2), co daje większy błąd zaokrąglania, a zatem nie jest pożądane. - user
@Matija, z matematycznego punktu widzenia, 3544.5249 zaokrąglone to 3544,52, a nie 3544,53, więc ten kod jest poprawny. Jeśli chcesz go zaokrąglić do 3544.53 w tym i takich przypadkach (nawet trudne jest niepoprawne), zrób coś takiego: number += 0.00011 - Bozidar Sikanjic


Żadna z odpowiedzi tutaj nie jest poprawna. @stinkycheeseman zapytał spędzać bydlęwszyscy zaokrągliłeś numer.

Aby zaokrąglić w górę, użyj tego:

Math.ceil(num * 100)/100;

53
2018-06-13 09:35



Przykładowe dane wejściowe i wyjściowe pokazują, że chociaż pytanie brzmiało "zaokrąglić w górę ...", faktycznie miało ono być "od rundy do ...". - JayDM
@stinkycheeseman wskazywał na błąd w konkretnym przypadku, nie chciał zawsze zaokrąglać, tak jak to robi, chciał tylko 0,005, aby zaokrąglić do 0,01 - mjaggard
Znalazłem dziwny błąd podczas testowania Math.ceil(1.1 * 100)/100; -Wraca 1.11, ponieważ 1.1 * 100 jest 110.00000000000001 według najnowszych nowoczesnych przeglądarek Firefox, Chrome, Safari i Opera ... IE, w starym stylu, wciąż myśli 1.1*100=1100. - skobaljic
@skobaljic try Math.ceil(num.toFixed(4) * 100) / 100 - treeface
@treeface Math.ceil((1.1).toFixed(4) * 100) / 100 również powróci 1.11 W Firefoksie nowoczesny problem / bug przeglądarki to mnożenie i ludzie powinni o tym wiedzieć (na przykład pracowałem nad grą loteryjną). - skobaljic


To pytanie jest skomplikowane.

Załóżmy, że mamy funkcję, roundTo2DP(num), który przyjmuje wartość float jako argument i zwraca wartość zaokrągloną do 2 miejsc po przecinku. Do czego powinno się odnosić każde z tych wyrażeń?

  • roundTo2DP(0.014999999999999999)
  • roundTo2DP(0.0150000000000000001)
  • roundTo2DP(0.015)

"Oczywistą" odpowiedzią jest to, że pierwszy przykład powinien zaokrąglić do 0,01 (ponieważ jest bliższy 0,01 niż 0,02), podczas gdy pozostałe dwa powinny zaokrąglić do 0,02 (ponieważ 0,0150000000000000001 jest bliższy 0.02 niż 0.01, a ponieważ 0.015 jest dokładnie w połowie drogi między je i istnieje konwencja matematyczna, że ​​takie liczby są zaokrąglane w górę).

Połów, który możesz odgadnąć, jest to roundTo2DP  nie może być wdrożone, aby dać te oczywiste odpowiedzi, ponieważ wszystkie trzy liczby przekazane do niego są ten sam numer. IEEE 754 binarne liczby zmiennoprzecinkowe (rodzaj używany przez JavaScript) nie może dokładnie reprezentować większości liczb niecałkowitych, więc wszystkie trzy literały liczbowe powyżej są zaokrąglane do najbliższej poprawnej liczby zmiennoprzecinkowej. Ta liczba, jak to się dzieje, jest dokładnie

0,01499999999999999944488848768742172978818416595458984375

który jest bliższy 0.01 niż 0.02.

Możesz zobaczyć, że wszystkie trzy liczby są takie same na twojej konsoli przeglądarki, powłoce węzła lub innym interprerze JavaScript. Porównaj je:

> 0,014999999999999999 === 0,0150000000000000001
true

Kiedy piszę m = 0.0150000000000000001, dokładna wartość mw którym kończę, jest bliższa 0.01 niż to jest 0.02. A jednak, jeśli się nawrócę m do łańcucha ...

> var m = 0,0150000000000000001;
> console.log (String (m));
0.015
> var m = 0,014999999999999999;
> console.log (String (m));
0.015

... Dostaję 0,015, która powinna zaokrąglić do 0,02, a która jest zauważalnie nie 56-miejscowy numer miejsce wcześniej powiedziałem, że wszystkie te liczby były dokładnie równe. Więc czym jest ta mroczna magia?

Odpowiedź można znaleźć w specyfikacji ECMAScript, w sekcji 7.1.12.1: ToString zastosowany do typu Number. Oto zasady konwersji niektórych liczb m do struny są określone. Kluczową częścią jest punkt 5, w którym liczba całkowita s jest generowany, którego cyfry będą używane w reprezentacji String m:

pozwolić n, k, i s być liczbami całkowitymi takimi, że k ≥ 1, 10k-1 ≤ s <10k, wartość Liczba dla s × 10n-k jest m, i k jest tak mały, jak to możliwe. Zauważ, że k jest liczbą cyfr w reprezentacji dziesiętnej s, to s nie jest podzielna przez 10, a najmniej znacząca cyfra s niekoniecznie jest jednoznacznie określone tymi kryteriami.

Kluczową częścią jest tutaj wymóg, że "k jest tak mały, jak to tylko możliwe. "To, co jest wymagane, jest wymogiem, który z uwagi na liczbę m, wartość String(m) muszę mieć najmniejsza możliwa liczba cyfr wciąż spełniając wymóg, że Number(String(m)) === m. Ponieważ już to wiemy 0.015 === 0.0150000000000000001, teraz jest jasne, dlaczego String(0.0150000000000000001) === '0.015' musi być prawdą.

Oczywiście żadna z tych dyskusji nie odpowiedziała bezpośrednio na pytanie roundTo2DP(m)  powinien powrót. Gdyby mDokładna wartość to 0,01499999999999999944488848768742172978818416595458984375, ale jej reprezentacja łańcuchów to "0,015", a następnie jaka jest poprawny odpowiedź - matematycznie, praktycznie, filozoficznie, czy cokolwiek - kiedy zaokrągla się ją do dwóch miejsc po przecinku?

Nie ma jednej prawidłowej odpowiedzi na to. To zależy od twojego przypadku użycia. Prawdopodobnie chcesz uszanować reprezentację napisów i zaokrąglić w górę, gdy:

  • Przedstawiona wartość jest z natury dyskretna, np. ilość waluty w dinarach z 3 miejscami po przecinku, jak dinary. W tym przypadku prawdziwe wartość liczby takiej jak 0.015 jest 0.015, a reprezentacja 0.0149999999 ..., że dostaje się w binarnym zmiennoprzecinkowym jest błędem zaokrąglania. (Oczywiście wielu będzie argumentować, rozsądnie, że powinieneś używać biblioteki dziesiętnej do obsługi takich wartości i nigdy nie reprezentujesz ich jako binarnych liczb zmiennoprzecinkowych w pierwszej kolejności.)
  • Wartość została wpisana przez użytkownika. W tym przypadku ponownie wprowadzona dokładna liczba dziesiętna jest bardziej "prawdziwa" niż najbliższa binarna reprezentacja zmiennoprzecinkowa.

Z drugiej strony, prawdopodobnie chcesz uszanować binarną wartość zmiennoprzecinkową i zaokrąglić w dół, gdy twoja wartość pochodzi z naturalnej skali ciągłej - na przykład, jeśli jest to odczyt z czujnika.

Te dwa podejścia wymagają innego kodu. Aby uszanować łańcuchową reprezentację liczby, możemy (z dość rozsądnym subtelnym kodem) zaimplementować własne zaokrąglenie, które działa bezpośrednio na reprezentację String, cyfra po cyfrze, używając tego samego algorytmu, którego używałbyś w szkole, kiedy uczono, jak zaokrąglać liczby. Poniżej znajduje się przykład, który respektuje wymóg OP reprezentowania liczby do 2 miejsc po przecinku "tylko wtedy, gdy jest to konieczne" poprzez usuwanie końcowych zer po przecinku dziesiętnym; możesz oczywiście dostosować go do swoich konkretnych potrzeb.

/**
 * Converts num to a decimal string (if it isn't one already) and then rounds it
 * to at most dp decimal places.
 *
 * For explanation of why you'd want to perform rounding operations on a String
 * rather than a Number, see http://stackoverflow.com/a/38676273/1709587
 *
 * @param {(number|string)} num
 * @param {number} dp
 * @return {string}
 */
function roundStringNumberWithoutTrailingZeroes (num, dp) {
    if (arguments.length != 2) throw new Error("2 arguments required");

    num = String(num);
    if (num.indexOf('e+') != -1) {
        // Can't round numbers this large because their string representation
        // contains an exponent, like 9.99e+37
        throw new Error("num too large");
    }
    if (num.indexOf('.') == -1) {
        // Nothing to do
        return num;
    }

    var parts = num.split('.'),
        beforePoint = parts[0],
        afterPoint = parts[1],
        shouldRoundUp = afterPoint[dp] >= 5,
        finalNumber;

    afterPoint = afterPoint.slice(0, dp);
    if (!shouldRoundUp) {
        finalNumber = beforePoint + '.' + afterPoint;
    } else if (/^9+$/.test(afterPoint)) {
        // If we need to round up a number like 1.9999, increment the integer
        // before the decimal point and discard the fractional part.
        finalNumber = Number(beforePoint)+1;
    } else {
        // Starting from the last digit, increment digits until we find one
        // that is not 9, then stop
        var i = dp-1;
        while (true) {
            if (afterPoint[i] == '9') {
                afterPoint = afterPoint.substr(0, i) +
                             '0' +
                             afterPoint.substr(i+1);
                i--;
            } else {
                afterPoint = afterPoint.substr(0, i) +
                             (Number(afterPoint[i]) + 1) +
                             afterPoint.substr(i+1);
                break;
            }
        }

        finalNumber = beforePoint + '.' + afterPoint;
    }

    // Remove trailing zeroes from fractional part before returning
    return finalNumber.replace(/0+$/, '')
}

Przykładowe użycie:

> roundStringNumberWithoutTrailingZeroes (1.6, 2)
'1.6'
> roundStringNumberWithoutTrailingZeroes (10000, 2)
'10000'
> roundStringNumberWithoutTrailingZeroes (0.015, 2)
'0.02'
> roundStringNumberWithoutTrailingZeroes ('0,015000', 2)
'0.02'
> roundStringNumberWithoutTrailingZeroes (1, 1)
'1'
> roundStringNumberWithoutTrailingZeroes ('0.015', 2)
'0.02'
> roundStringNumberWithoutTrailingZeroes (0.01499999999999999944488848768742172978818416595458984375, 2)
'0.02'
> roundStringNumberWithoutTrailingZeroes ('0,01499999999999999944488848768742172978818416595458984375', 2)
'0.01'

Powyższa funkcja to prawdopodobnie czego chcesz użyć, aby uniknąć sytuacji, w których użytkownicy będą świadkami błędnie zaokrąglonych liczb.

(Alternatywnie możesz też spróbować round10 biblioteka, która zapewnia podobną funkcję z bardzo odmienną implementacją).

Ale co, jeśli masz drugi rodzaj liczby - wartość zaczerpniętą z ciągłej skali, gdzie nie ma powodu, aby sądzić, że przybliżone dziesiętne reprezentacje z mniejszą liczbą miejsc dziesiętnych są bardziej dokładny niż ci z więcej? W takim przypadku my nie rób tego chcą szanować reprezentację String, ponieważ ta reprezentacja (jak wyjaśniono w specyfikacji) jest już zaokrąglona; nie chcemy popełnić błędu mówiąc: "0,014999999 ... 375 rund do 0,015, które zaokrąglają do 0,02, czyli 0,014999999 ... 375 rund do 0,02".

Tutaj możemy po prostu użyć wbudowanego toFixed metoda. Zauważ, że dzwoniąc Number() na ciągu zwracanym przez toFixed, otrzymujemy numer, którego reprezentacja ciągów nie ma końcowych zer (dzięki temu, że JavaScript oblicza reprezentację ciągu liczb, omówioną wcześniej w tej odpowiedzi).

/**
 * Takes a float and rounds it to at most dp decimal places. For example
 *
 *     roundFloatNumberWithoutTrailingZeroes(1.2345, 3)
 *
 * returns 1.234
 *
 * Note that since this treats the value passed to it as a floating point
 * number, it will have counterintuitive results in some cases. For instance,
 * 
 *     roundFloatNumberWithoutTrailingZeroes(0.015, 2)
 *
 * gives 0.01 where 0.02 might be expected. For an explanation of why, see
 * http://stackoverflow.com/a/38676273/1709587. You may want to consider using the
 * roundStringNumberWithoutTrailingZeroes function there instead.
 *
 * @param {number} num
 * @param {number} dp
 * @return {number}
 */
function roundFloatNumberWithoutTrailingZeroes (num, dp) {
    var numToFixedDp = Number(num).toFixed(dp);
    return Number(numToFixedDp);
}

53
2017-07-30 16:47



To nie działa w niektórych skrajnych przypadkach: spróbuj (jsfiddle) z roundStringNumberWithoutTrailingZeroes(362.42499999999995, 2). Oczekiwany wynik (jak w PHP echo round(362.42499999999995, 2)): 362.43. Aktualny rezultat: 362.42 - Dr. Gianluigi Zane Zanettini
@ Dr.GianluigiZaneZanettini Huh. Dziwne. Nie jestem pewien, dlaczego PHP round daje 362,43. To wydaje się intuicyjnie błędne, ponieważ 362.42499999999995 jest mniejsze niż 362.425 (w matematyce iw kodzie - 362.42499999999995 < 362.425 jest prawdziwe zarówno w JS, jak i PHP). Ani odpowiedź PHP nie pozwala zminimalizować odległości między oryginalnymi i zaokrąglonymi liczbami zmiennoprzecinkowymi, ponieważ 362.43 - 362.42499999999995 > 362.42499999999995 - 362.42. Według php.net/manual/en/function.round.php, PHP round podąża za standardem C99; Będę musiał udać się do krainy C, aby zrozumieć, co się dzieje. - Mark Amery
Po obejrzeniu implementacja zaokrąglania w źródle PHPNie mam zielonego pojęcia, co robi. To potwornie skomplikowana implementacja pełna makr i gałęzi oraz konwersja ciągów i rozgałęzianie na zakodowane magiczne wartości precyzyjne. Nie jestem pewien, co powiedzieć poza "odpowiedź PHP jest rażąco źle, i powinniśmy zgłosić raport o błędzie". Jak przy okazji, znalazłeś numer 362.42499999999995? - Mark Amery
@ Dr.GianluigiZaneZanettini Stworzyłem raport o błędzie: bugs.php.net/bug.php?id=75644 - Mark Amery
Właściwie myślałem, że PHP jest poprawne, ponieważ 362.42499999999995 kończy się na "5", więc liczba musi iść "w górę", a nie "w dół". Czy mam jakiś sens? Dzięki za twoje wysiłki! Znalazłem numer z rabatem% do ceny. - Dr. Gianluigi Zane Zanettini


Rozważać .toFixed() i .toPrecision():

http://www.javascriptkit.com/javatutors/formatnumber.shtml


51
2017-08-06 17:21



toFixed dodaje dziesiętne punkty do każdej wartości bez względu na wszystko. - stinkycheeseman
Oba są tutaj bezużyteczne - Esailija
Niestety, obie funkcje dodadzą dodatkowe miejsca po przecinku, które wydają się nie być pożądane przez @stinkycheeseman. - jackwanders
stackoverflow.com/questions/566564/... - Shreedhar
zwracają łańcuchy, a nie liczby, więc formatują i nie obliczają. - saimiris_devel


Oto prosty sposób na zrobienie tego:

Math.round(value * 100) / 100

Możesz chcieć wykonać osobną funkcję, aby zrobić to za Ciebie:

function roundToTwo(value) {
    return(Math.round(value * 100) / 100);
}

Wtedy po prostu przekazujesz wartość.

Można go zaokrąglić do dowolnej dowolnej liczby miejsc po przecinku, dodając drugi parametr.

function myRound(value, places) {
    var multiplier = Math.pow(10, places);

    return (Math.round(value * multiplier) / multiplier);
}

42
2017-08-06 17:27



To powinno być najbardziej głosowali odpowiedź. - jose.angel.jimenez
To rozwiązanie jest złe, patrz stackoverflow.com/questions/38322372/... jeśli wpiszesz 156893.145 i zaokrąglasz ją za pomocą powyższej funkcji, otrzymasz 156893.14 zamiast 156893.15 !!! - saimiris_devel


+(10).toFixed(2); // = 10
+(10.12345).toFixed(2); // = 10.12

(10).toFixed(2); // = 10.00
(10.12345).toFixed(2); // = 10.12

35
2018-06-25 10:27



Nie zawsze da to takie same wyniki, jakie uzyskalibyśmy, gdybyście wzięli reprezentację String liczby i zaokrągli ją. Na przykład, +(0.015).toFixed(2) == 0.01. - Mark Amery