Usunąć nieznaczące zera na końcu liczby?

157

Czy przegapiłem standardowe wywołanie interfejsu API, które usuwa końcowe nieznaczące zera z liczby?

Dawny.

var x = 1.234000 // to become 1.234;
var y = 1.234001; // stays 1.234001

Number.toFixed () i Number.toPrecision () nie są tym, czego szukam.

Steven
źródło
6
Hm 1.234000 === 1.234.
Gumbo
5
Tak, jeśli zrobisz alert (x), pojawi się bez końcowych zer
JKirchartz
50
Po pracy z wieloma klientami mogę zaświadczyć, że nawet jeśli 1,234000 === 1,234, klienci nie chcą widzieć tych dodatkowych zer, jeśli nie muszą.
contactmatt
16
Użyj parseFloat(n)?
Mr. Alien
To było najłatwiejsze rozwiązanie, które obejmowało dla mnie wszystkie skrajne przypadki, dziękuję @ Mr. Alien.
James Perih

Odpowiedzi:

140

Jeśli przekonwertujesz go na ciąg, nie wyświetli żadnych końcowych zer, które nie są przechowywane w zmiennej w pierwszej kolejności, ponieważ została utworzona jako liczba, a nie łańcuch.

var n = 1.245000
var noZeroes = n.toString() // "1.245" 
Cristian Sanchez
źródło
6
Miałem właśnie wysłać kod, aby usunąć zera, ale rozwiązanie Daniela wydaje się działać. Działa nawet w przypadku ciągów znaków, takich jak „1.2345000”. ("1.2345000" * 1) .toString (); // zmienia się na 1,2345
Steven
7
Oczywiście, jeśli twoja zmienna jest już ciągiem, musisz najpierw przekonwertować ją na liczbę, a następnie z powrotem
derekcohen
To działało świetnie. Miałem ten sam problem, przekonwertowałem zmiennoprzecinkowy na ciąg znaków, a następnie parseFloat, aby przywrócić go w odpowiednim formacie, tylko bez końcowych zer.
Matt West
10
To nie jest kompletne rozwiązanie. To nie działa, gdy masz zmienną z pewnym błędem reprezentacji zmiennoprzecinkowej, na przykład: var n = 1.245000000000001(zakładając, że jest to nieistotne dla użytkownika)
augur
7
Może dawać nieoczekiwane wyniki dla liczb takich jak: 1231111111111111111111111111122222222222222222222222222222222222222222222222.00. Reprezentacja ciągu będzie w formacie wykładniczym: 1.231111111111111e + 81
Stas Makutin
227

Miałem podobny przypadek, w którym chciałem użyć w .toFixed()razie potrzeby, ale nie chciałem wyściółki, gdy nie było. Skończyło się na użyciu parseFloat w połączeniu z toFixed.

Naprawiono bez wypełnienia

parseFloat(n.toFixed(4));

Inna opcja, która robi prawie to samo
Ta odpowiedź może pomóc w podjęciu decyzji

Number(n.toFixed(4));

toFixedzaokrągli / dopełni liczbę do określonej długości, ale także zamieni ją na ciąg. Konwersja tego z powrotem na typ liczbowy nie tylko sprawi, że liczba będzie bezpieczniejsza w użyciu arytmetycznym, ale także automatycznie usunie wszystkie końcowe zera. Na przykład:

var n = "1.234000";
    n = parseFloat(n);
 // n is 1.234 and in number form

Ponieważ nawet jeśli zdefiniujesz liczbę z końcowymi zerami, zostaną one usunięte.

var n = 1.23000;
 // n == 1.23;
Gary
źródło
Zauważ, że działa to tylko wtedy, gdy odpowiednia liczba miejsc po przecinku jest równa lub większa niż argument toFixed. Jeśli liczba to na przykład 1.200000, to wynik funkcji toFixed (3) wyniesie 1.200, a zatem żadne zera na końcu nie zostaną usunięte.
Leopoldo Sanczyk
@LeopoldoSanczyk Nie, jest to prawdą tylko wtedy, gdy używasz tylko toFixed, ponieważ zwraca ciąg. Typy liczbowe automatycznie tracą końcowe zera. Dlatego użyłem tych dwóch w tandemie.
Gary
@Gary, wycofuję swój komentarz, parseFloat(n).toFixed(4);zamiast rzeczywistej odpowiedzi widziałem . Przykro mi.
Leopoldo Sanczyk
2
Dlaczego nie +(n.toFixed(4))?
Константин Ван
3
przegłosowano! tylko jeden problem z twoją odpowiedzią 0,00000005 jest konwertowany na 5e-8 jak mogę usunąć nieznaczące zera bez tego problemu, pracuję z małymi liczbami, takimi jak 0,000058000, gdzie zera na końcu muszą zostać usunięte
PirateApp
29

Najpierw użyłem kombinacji odpowiedzi Matti-Lyry i Gary'ego:

r=(+n).toFixed(4).replace(/\.0+$/,'')

Wyniki:

  • 1234870.98762341: "1234870.9876"
  • 1230009100: „1230009100”
  • 0,0012234: „0,0012”
  • 0,1200234: „0,12”
  • 0,000001231: „0”
  • 0,10001: „0,1000”
  • „asdf”: „NaN” (więc brak błędu w czasie wykonywania)

Dość problematyczny przypadek to 0,10001. Skończyło się na tej dłuższej wersji:

    r = (+n).toFixed(4);
    if (r.match(/\./)) {
      r = r.replace(/\.?0+$/, '');
    }
  • 1234870.98762341: "1234870.9876"
  • 1230009100: „1230009100”
  • 0,0012234: „0,0012”
  • 0,1200234: „0,12”
  • 0,000001231: „0”
  • 0,10001: „0,1”
  • „asdf”: „NaN” (więc brak błędu w czasie wykonywania)

Aktualizacja : A to jest nowsza wersja Gary'ego (patrz komentarze):

r=(+n).toFixed(4).replace(/([0-9]+(\.[0-9]+[1-9])?)(\.?0+$)/,'$1')

Daje to takie same wyniki, jak powyżej.

w00t
źródło
1
Mam czyste rozwiązanie regex, które moim zdaniem działatoFixed(4).replace(/([0-9]+(\.[1-9]+)?)(\.?0+$)/,"$1")
Gary,
1
Gah! Najwyraźniej nie jestem tak dobry w łamaniu własnych RegExes jak inni. Nie pozwolę, żeby mnie to pokonało! Przeprowadziłem ten test ze wszystkimi twoimi przypadkami testowymi plus każdy (ważny), o którym ([0-9]+(\.[0-9]+[1-9])?)(\.?0+$)
Gary
1
Wyrażenie regularne jest chciwe, a czasami usuwa „0” z części całkowitej! :-( Dlaczego nie tylko replace(/[,.][1-9]+(0+)/,'')?
Sebastian Sebald
1
Niedoceniona odpowiedz na to pytanie.
Charlie
1
@ w00t Och, przepraszam, teraz rozumiem. Byłem sposobem, w jaki testowałem, kiedy zrobiłem swój (patrz link powyżej). Twój nie zawodzi, gdy używasz toFixed, ponieważ liczba na pewno ma kropkę, ale zawiodła w moich testach, ponieważ chciałem, aby wyrażenie regularne działało dla dowolnej liczby, w tym liczb całkowitych bez kropki. Bez toFixed zjada zero na końcu. Mój błąd.
geekley
22

W toFixedrazie potrzeby metoda dokona odpowiedniego zaokrąglenia. Doda również końcowe zera, co nie zawsze jest idealne.

(4.55555).toFixed(2);
//-> "4.56"

(4).toFixed(2);
//-> "4.00"

Jeśli rzucisz wartość zwracaną na liczbę, te końcowe zera zostaną usunięte. Jest to prostsze podejście niż wykonywanie własnych operacji matematycznych zaokrągleń lub obcięć.

+(4.55555).toFixed(2);
//-> 4.56

+(4).toFixed(2);
//-> 4
CJ
źródło
1
trudne, ale dokładnie to, czego szukałem: usunięcie znaczących zer końcowych (które oczywiście zrobiliśmy znaczące przez toFixedwywołanie). to dziwny przypadek UX edge.
worc
12

Miałem zasadniczo te same wymagania i stwierdziłem, że nie ma wbudowanego mechanizmu dla tej funkcji.

Oprócz obcięcia końcowych zer, musiałem również zaokrąglić i sformatować dane wyjściowe dla bieżących ustawień regionalnych użytkownika (tj. 123 456,789).

Cała moja praca nad tym została zawarta jako prettyFloat.js (licencja MIT) na GitHub: https://github.com/dperish/prettyFloat.js


Przykłady użycia:

prettyFloat(1.111001, 3) // "1.111"
prettyFloat(1.111001, 4) // "1.111"
prettyFloat(1.1111001, 5) // "1.1111"
prettyFloat(1234.5678, 2) // "1234.57"
prettyFloat(1234.5678, 2, true) // "1,234.57" (en-us)


Aktualizacja - sierpień 2018


Wszystkie nowoczesne przeglądarki obsługują teraz interfejs API ECMAScript Internationalization , który zapewnia porównywanie ciągów tekstowych z uwzględnieniem języka, formatowanie liczb oraz formatowanie daty i godziny.

let formatters = {
    default: new Intl.NumberFormat(),
    currency: new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', minimumFractionDigits: 0, maximumFractionDigits: 0 }),
    whole: new Intl.NumberFormat('en-US', { style: 'decimal', minimumFractionDigits: 0, maximumFractionDigits: 0 }),
    oneDecimal: new Intl.NumberFormat('en-US', { style: 'decimal', minimumFractionDigits: 1, maximumFractionDigits: 1 }),
    twoDecimal: new Intl.NumberFormat('en-US', { style: 'decimal', minimumFractionDigits: 2, maximumFractionDigits: 2 })
};

formatters.twoDecimal.format(1234.5678);  // result: "1,234.57"
formatters.currency.format(28761232.291); // result: "$28,761,232"

W przypadku starszych przeglądarek możesz użyć tego wypełnienia: https://cdn.polyfill.io/v2/polyfill.min.js?features=Intl.~locale.en

dperish
źródło
To jest to, czego szukaliśmy. Musisz utworzyć pakiet npm.
EnZo
Więc prawdopodobnie powinienem to zaktualizować, ponieważ jest to teraz możliwe za pośrednictwem interfejsu API internacjonalizacji.
dperish
2
Jest to bardzo pomocne, dzięki za udostępnienie, a także dzięki za aktualizację.
jaggedsoft,
11

A co powiesz na pomnożenie przez jeden w ten sposób?

var x = 1.234000*1; // becomes 1.234

var y = 1.234001*1; // stays as 1.234001
Howard
źródło
9

Możesz spróbować tego, aby zminimalizować liczby zmienne

var n = 0.0000;
n = parseFloat(n.toString()); 

//output n = 0; 
// n = 3.14000; --> n = 3.14;
Amit Panasara
źródło
7

Czysta odpowiedź regex

n.replace(/(\.[0-9]*[1-9])0+$|\.0*$/,'$1');

Zastanawiam się, dlaczego nikt nikogo nie dał!

João Costa
źródło
Nie będzie działać z liczbami (liczby są podane w oryginalnym pytaniu). Uwielbiam jednak rozwiązanie do reprezentacji ciągu!
BennyHilarious
6

Musiałem rozwiązać ten problem również, gdy Django wyświetlał wartości typu dziesiętnego w polu tekstowym. Np. Kiedy „1” było wartością. To pokaże „1,00000000”. Gdyby wartość „1,23” była wartością „1,23000000” (w przypadku ustawienia „miejsca dziesiętne” na 8)

Używanie parseFloat nie było dla mnie opcją, ponieważ możliwe, że nie zwraca dokładnie tej samej wartości. toFixed nie było opcją, ponieważ nie chciałem niczego zaokrąglać, więc utworzyłem funkcję:

function removeTrailingZeros(value) {
    value = value.toString();

    # if not containing a dot, we do not need to do anything
    if (value.indexOf('.') === -1) {
        return value;
    }

    # as long as the last character is a 0 or a dot, remove it
    while((value.slice(-1) === '0' || value.slice(-1) === '.') && value.indexOf('.') !== -1) {
        value = value.substr(0, value.length - 1);
    }
    return value;
}
megasnort
źródło
Działa to, jednak jest dość nieefektywne, ponieważ powoduje tworzenie wielu ciągów na wykonanie. Wyobraź sobie, że uruchamiasz to na wszystkich komórkach w tabeli, gdzie dobra liczba to 0,00000. Lepszym rozwiązaniem byłoby określenie liczby końcowych zer, a następnie wykonanie wycinka za jednym razem. Określenie, czym jest znak, jest w dużej mierze wydajne, dzielenie ciągów jest mniej.
Derek Nigel Bartram
Zrobiłem coś podobnego do twojej sugestii Derek: github.com/rek/remove-trailing-zeros/blob/master/…
rekarnar
Test wydajności jest tutaj: jsperf.com/remove-trailing-zeros/1 , miałeś rację, znajdowanie zer i usuwanie ich jest znacznie szybsze!
rekarnar
4

Żadne z tych rozwiązań nie działało dla mnie w przypadku bardzo małych liczb. http://numeraljs.com/ rozwiązało to dla mnie.

parseFloat(0.00000001.toFixed(8));
// 1e-8

numeral(0.00000001).format('0[.][00000000]');
// "0.00000001"
Devon
źródło
Nie może obsłużyć więcej niż 6 precyzji. Powiedziałbym, że ta biblioteka jest bezużyteczna, jeśli jest przeznaczona dla małych liczb. Zdecydowanie nie polecam.
Daniel Kmak,
2

Jeśli z jakiegoś powodu nie możesz użyć zmiennych typu Floats (np. W przypadku przepływów pieniężnych) i już zaczynasz od łańcucha reprezentującego poprawną liczbę, to rozwiązanie może okazać się przydatne. Konwertuje łańcuch reprezentujący liczbę na łańcuch reprezentujący liczbę bez końcowych zer.

function removeTrailingZeroes( strAmount ) {
    // remove all trailing zeroes in the decimal part
    var strDecSepCd = '.'; // decimal separator
    var iDSPosition = strAmount.indexOf( strDecSepCd ); // decimal separator positions
    if ( iDSPosition !== -1 ) {
        var strDecPart = strAmount.substr( iDSPosition ); // including the decimal separator

        var i = strDecPart.length - 1;
        for ( ; i >= 0 ; i-- ) {
            if ( strDecPart.charAt(i) !== '0') {
                break;
            }
        }

        if ( i=== 0 ) {
            return strAmount.substring(0, iDSPosition);
        } else {
            // return INTPART and DS + DECPART including the rightmost significant number
            return strAmount.substring(0, iDSPosition) + strDecPart.substring(0,i + 1);
        }
    }

    return strAmount;
}
BennyHilarious
źródło
0

Po przeczytaniu wszystkich odpowiedzi - i komentarzy - skończyło się na tym:

function isFloat(n) {
    let number = (Number(n) === n && n % 1 !== 0) ? eval(parseFloat(n)) : n;
    return number;
}

Wiem, że używanie evalmoże być w jakiś sposób szkodliwe, ale bardzo mi to pomogło.

Więc:

isFloat(1.234000);     // = 1.234;
isFloat(1.234001);     // = 1.234001
isFloat(1.2340010000); // = 1.234001

Jeśli chcesz ograniczyć miejsca dziesiętne, użyj tego, toFixed()co wskazali inni.

let number = (Number(n) === n && n % 1 !== 0) ? eval(parseFloat(n).toFixed(3)) : n;

Otóż ​​to.

ed1nh0
źródło
0

Musiałem usunąć wszystkie zera końcowe, ale zachować co najmniej 2 miejsca po przecinku, w tym zera.
Liczby, z którymi pracuję, to 6 ciągów liczb dziesiętnych, generowanych przez .toFixed (6).

Spodziewany wynik:

var numstra = 12345.000010 // should return 12345.00001
var numstrb = 12345.100000 // should return 12345.10
var numstrc = 12345.000000 // should return 12345.00
var numstrd = 12345.123000 // should return 12345.123

Rozwiązanie:

var numstr = 12345.100000

while (numstr[numstr.length-1] === "0") {           
    numstr = numstr.slice(0, -1)
    if (numstr[numstr.length-1] !== "0") {break;}
    if (numstr[numstr.length-3] === ".") {break;}
}

console.log(numstr) // 12345.10

Logika:

Uruchom funkcję pętli, jeśli ostatni znak łańcucha to zero.
Usuń ostatni znak i zaktualizuj zmienną łańcuchową.
Jeśli zaktualizowany łańcuch ostatni znak nie jest zerem, kończy się pętla.
Jeśli zaktualizowany łańcuch od trzeciego do ostatniego znaku jest zmiennoprzecinkowym, pętla końcowa.

Kevin H.
źródło
-1

Oto możliwe rozwiązanie:

var x = 1.234000 // to become 1.234;
var y = 1.234001; // stays 1.234001

eval(x) --> 1.234
eval(y) --> 1.234001
Księżyc
źródło
4
Nie potrzebujesz eval do tego prostego zadania! parseFloat()będzie bardzo odpowiedni.
hutchbat
6
eval == evil. Nie używaj go nigdy
eagor