Usuń zera końcowe

177

Mam niektóre pola zwrócone przez kolekcję jako

2.4200
2.0044
2.0000

Chcę takich wyników

2.42
2.0044
2

Próbowałem z String.Format, ale wraca 2.0000i ustawia go na N0zaokrąglenie również innych wartości.

Zo Has
źródło
3
początkowo typ rekordu to string ??
Singleton
2
Zobacz moją odpowiedź: String.Format()z 'G' powinno dostać to, czego chcesz. Zaktualizowałem moją odpowiedź z linkiem do standardowych formatów liczbowych. Bardzo przydatne.
Uszy psa
3
Można rzutować na liczbę dziesiętną double, a wartość domyślna ToStringdouble emituje końcowe zera. przeczytaj to
Shimmy Weitzhandler,
2
I prawdopodobnie będzie to kosztować mniej wydajności (w przypadku bardzo dużej liczby rekordów) niż przekazywanie funkcji „G” jako formatu ciągu znaków ToString.
Shimmy Weitzhandler,
4
Nie należy zamieniać ułamka dziesiętnego na podwójny, straci to na znaczeniu i może wprowadzić potęgę dwóch niedokładności zaokrągleń.
kristianp

Odpowiedzi:

167

Czy nie jest to takie proste, jeśli dane wejściowe są ciągiem znaków? Możesz użyć jednego z tych:

string.Format("{0:G29}", decimal.Parse("2.0044"))

decimal.Parse("2.0044").ToString("G29")

2.0m.ToString("G29")

Powinno to działać dla wszystkich danych wejściowych.

Aktualizacja Sprawdź standardowe formaty liczbowe , które musiałem jawnie ustawić specyfikator dokładności na 29, ponieważ dokumentacja jasno stwierdza:

Jeśli jednak liczba jest liczbą dziesiętną, a specyfikator dokładności jest pominięty, zawsze używana jest notacja stałoprzecinkowa, a końcowe zera są zachowywane

Aktualizacja Konrad zaznaczył w komentarzach:

Uważaj na wartości takie jak 0,000001. Format G29 przedstawi je w możliwie najkrótszy sposób, więc przełączy się na notację wykładniczą. string.Format("{0:G29}", decimal.Parse("0.00000001",System.Globalization.CultureInfo.GetCultureInfo("en-US")))da jako wynik „1E-08”.

Uszy psa
źródło
1
Ze wszystkich odpowiedzi, twoja wymagała najmniejszego wysiłku. Dziękuję Dog Ears.
Zo ma
4
var d = 2,0 m; d.ToString („G”);
Dave Hillier
3
@Dave Hillier - Rozumiem, poprawiłem odpowiedź. Twoje zdrowie. [dodano wyraźny „specyfikator precyzji”]
Dog Ears
16
Uważaj na wartości takie jak 0,000001. Format G29 przedstawi je w możliwie najkrótszy sposób, więc przełączy się na notację wykładniczą. string.Format ("{0: G29}", decimal.Parse ("0.00000001", System.Globalization.CultureInfo.GetCultureInfo ("en-US"))) zwróci jako wynik „1E-08”
Konrad
15
@Konrad - czy jest sposób na uniknięcie notacji naukowej dla liczb, które mają 5 lub 6 miejsc po przecinku?
Jill,
202

Napotkałem ten sam problem, ale w przypadku, gdy nie mam kontroli nad wyjściem do łańcucha, którym zajęła się biblioteka. Po przyjrzeniu się szczegółom implementacji typu Decimal (patrz http://msdn.microsoft.com/en-us/library/system.decimal.getbits.aspx ) wpadłem na zgrabną sztuczkę (tutaj jako rozszerzenie metoda):

public static decimal Normalize(this decimal value)
{
    return value/1.000000000000000000000000000000000m;
}

Wykładnik dziesiętny jest zredukowany do tego, co jest potrzebne. Wywołanie ToString () na wyjściu dziesiętnym zapisze liczbę bez końcowego 0. Np

1.200m.Normalize().ToString();
Thomas Materna
źródło
29
Ta odpowiedź jest własna, ponieważ w przeciwieństwie do praktycznie każdej innej odpowiedzi na to pytanie (a nawet całego tematu) jest faktycznie DZIAŁA i robi to, o co prosi OP.
Coxy
2
czy liczba zer jest tutaj dokładną wielkością, czy po prostu pokrywa większość oczekiwanych wartości?
Simon_Weaver,
3
MSDN stwierdza: „Współczynnikiem skalowania jest niejawnie liczba 10, podniesiona do wykładnika z zakresu od 0 do 28”, co rozumiem, że liczba dziesiętna będzie miała co najwyżej 28 cyfr po przecinku. Dowolna liczba zer> = 28 powinna działać.
Thomas Materna
2
To najlepsza odpowiedź! Liczba w odpowiedzi ma 34 cyfry, 1a po niej 33 cyfry, 0ale to tworzy dokładnie to samo decimalwystąpienie, co liczba z 29 cyframi, jedną 1i 28 0znakami. Tak jak powiedział @ThomasMaterna w swoim komentarzu. Nie System.Decimalmoże mieć więcej niż 29 cyfr (liczby z początkowymi cyframi większe niż 79228...mogą mieć łącznie tylko 28 cyfr). Jeśli chcesz mieć więcej końcowych zer, pomnóż przez 1.000...mzamiast dzielić. Można również Math.Roundodciąć kilka zer, na przykład Math.Round(27.40000m, 2)daje 27.40m, więc zostało tylko jedno zero.
Jeppe Stig Nielsen
2
Świetna sztuczka, ale NIE działa na Mono i nie jest gwarantowana do pracy z wersjami funkcji .NET
Bigjim
87

Moim zdaniem bezpieczniej jest używać niestandardowych ciągów formatu liczbowego .

decimal d = 0.00000000000010000000000m;
string custom = d.ToString("0.#########################");
// gives: 0,0000000000001
string general = d.ToString("G29");
// gives: 1E-13
Michał Powaga
źródło
25
+1 za jedyną odpowiedź, która opiera się na stabilnym, udokumentowanym zachowaniu.
sinelaw
3
Powinieneś umieścić tam 28 znaków '#', patrz blackwasp.co.uk/DecimalTrailingZeroes.aspx .
Jaime Hablutzel
32

Używam tego kodu, aby uniknąć naukowej notacji „G29”:

public static string DecimalToString(this decimal dec)
{
    string strdec = dec.ToString(CultureInfo.InvariantCulture);
    return strdec.Contains(".") ? strdec.TrimEnd('0').TrimEnd('.') : strdec;
}

EDYCJA: przy użyciu systemu CultureInfo.NumberFormat.NumberDecimalSeparator:

public static string DecimalToString(this decimal dec)
{
    string sep = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator;
    string strdec = dec.ToString(CultureInfo.CurrentCulture);
    return strdec.Contains(sep) ? strdec.TrimEnd('0').TrimEnd(sep.ToCharArray()) : strdec;
}
x7BiT
źródło
1
To wspaniała odpowiedź. Jest to bardzo proste i możesz dokładnie zobaczyć, co się dzieje, zamiast używać magicznego programu do formatowania ciągów.
Timothy Gonzalez
9
Fajnie, ale nie będzie działać w przypadku kultur używających przecinka jako separatora dziesiętnego. Musisz użyćCulture.NumberFormat.NumberDecimalSeparator
blearyeye
1
Najlepsze podejście. Wszystko inne jest niepotrzebnie skomplikowane. Pamiętaj tylko, aby zawsze sprawdzić, czy Twój numer zawiera przecinek dziesiętny.
RRM
1
Aby być rozszerzeniem, brakuje „this” przed parametrem: public static string DecimalToString (this decimal dec)
João Antunes
Bardzo dziękuję za komentarze. Zaktualizowałem odpowiedź o nową wersję ze wskazówkami.
x7BiT
19

Użyj #symbolu hash ( ), aby wyświetlać końcowe 0 tylko wtedy, gdy jest to konieczne. Zobacz testy poniżej.

decimal num1 = 13.1534545765;
decimal num2 = 49.100145;
decimal num3 = 30.000235;

num1.ToString("0.##");       //13.15%
num2.ToString("0.##");       //49.1%
num3.ToString("0.##");       //30%
Fizzix
źródło
7
Nie ma odpowiedzi. Nie usuwasz końcowych zer, usuwasz precyzję. Proponowana metoda num1.ToString("0.##")powinna zwrócić 13.1534545765(bez zmian), ponieważ nie ma żadnych końcowych zer.
Jan 'splite' K.
I właśnie dlatego wyświetlam odpowiedzi na moje trzy proponowane dane wejściowe, aby użytkownicy mogli zobaczyć, jak działa moje rozwiązanie.
Fizzix
Nie odpowiada na pytanie, nawet jeśli wymienisz proponowane dane wejściowe.
Dave Friedel
2
Głosowałbym za tym 10 razy, gdybym mógł. Dokładnie to, czego potrzebowałem!
SubqueryCrunch
1
Doskonała odpowiedź na pytanie. NAJLEPSZA odpowiedź moim zdaniem. Komentarz, że usuwa precyzję, jest poprawny, ale nie o to chodziło. Najważniejsze - tego potrzebuję. Moi użytkownicy zazwyczaj wprowadzają liczby całkowite, może około 2,5, ale muszę obsługiwać 3 cyfry poza przecinkiem. Dlatego chcę im podać to, co wpisali, a nie kilka zer, których nie wprowadzili. np. Console.Write ($ "num3 = {num3: 0. ##}"); => num3 = 30
bobwki
2

Podejście na bardzo niskim poziomie, ale uważam, że byłby to najbardziej wydajny sposób przy użyciu tylko szybkich obliczeń całkowitych (bez powolnego analizowania ciągów i metod uwzględniających kulturę):

public static decimal Normalize(this decimal d)
{
    int[] bits = decimal.GetBits(d);

    int sign = bits[3] & (1 << 31);
    int exp = (bits[3] >> 16) & 0x1f;

    uint a = (uint)bits[2]; // Top bits
    uint b = (uint)bits[1]; // Middle bits
    uint c = (uint)bits[0]; // Bottom bits

    while (exp > 0 && ((a % 5) * 6 + (b % 5) * 6 + c) % 10 == 0)
    {
        uint r;
        a = DivideBy10((uint)0, a, out r);
        b = DivideBy10(r, b, out r);
        c = DivideBy10(r, c, out r);
        exp--;
    }

    bits[0] = (int)c;
    bits[1] = (int)b;
    bits[2] = (int)a;
    bits[3] = (exp << 16) | sign;
    return new decimal(bits);
}

private static uint DivideBy10(uint highBits, uint lowBits, out uint remainder)
{
    ulong total = highBits;
    total <<= 32;
    total = total | (ulong)lowBits;

    remainder = (uint)(total % 10L);
    return (uint)(total / 10L);
}
Bigjim
źródło
1
Próbowałem przejść tę trasę, ale zoptymalizowałem ją dalej, więc stała się naprawdę znacznie szybsza niż twoja, np. Nie dzieląc intercji hi i mid (twoich ai b) w każdej chwili iteracji, jeśli i tak wszystkie są równe zero. Jednak wtedy znalazłem to zaskakująco proste rozwiązanie, które z łatwością pokonuje to, co ty i ja wymyśliliśmy pod względem wydajności .
Eugene Beresovsky
2

To zadziała:

decimal source = 2.4200m;
string output = ((double)source).ToString();

Lub jeśli Twoja wartość początkowa to string:

string source = "2.4200";
string output = double.Parse(source).ToString();

Zwróć uwagę na ten komentarz .

Shimmy Weitzhandler
źródło
2
@ Damien, tak, i zauważ, że dla tych puropoz (nic nie poczujesz, chyba że zrobisz miliard rekordów), po pierwsze dlatego, że double jest szybsze, po drugie, ponieważ przekazanie formatu ciągu do funkcji ToString kosztuje więcej wydajności niż nie przekazywanie parametrów. znowu nic nie poczujesz, dopóki nie będziesz pracować nad miliardami płyt.
Shimmy Weitzhandler,
1
Nie musisz określać G, double.ToStringponieważ domyślnie usuwa końcowe zera.
Shimmy Weitzhandler,
4
Uważaj na wartości takie jak 0,000001. Zostaną one przedstawione w notacji wykładniczej. double.Parse ("0.00000001", System.Globalization.CultureInfo.GetCultureInfo ("en-US")). ToString () da wynik "1E-08"
Konrad
17
NIGDY tego nie rób. Zwykle powodem, dla którego masz ułamek dziesiętny, jest to, że precyzyjnie reprezentujesz liczbę (a nie w przybliżeniu tak, jak podwójna). To prawda, ten błąd zmiennoprzecinkowy jest prawdopodobnie dość mały, ale może spowodować wyświetlanie nieprawidłowych liczb, niemniej jednak
Adam Tegen
2
Ups, konwersja 128-bitowego typu danych (dziesiętnego) na 64-bitowy typ danych (podwójny) w celu formatowania nie jest dobrym pomysłem!
avl_sweden,
2

To jest proste.

decimal decNumber = Convert.ToDecimal(value);
        return decNumber.ToString("0.####");

Przetestowany.

Twoje zdrowie :)

Rakesh
źródło
1

Zależy od tego, co reprezentuje twoja liczba i jak chcesz zarządzać wartościami: czy to waluta, czy potrzebujesz zaokrąglenia, czy obcięcia, czy potrzebujesz tego zaokrąglenia tylko do wyświetlenia?

Jeśli do wyświetlania rozważ formatowanie liczb w postaci x.ToString („”)

http://msdn.microsoft.com/en-us/library/dwhawy9k.aspx i

http://msdn.microsoft.com/en-us/library/0c899ak8.aspx

Jeśli jest to tylko zaokrąglanie, użyj przeciążenia Math.Round, które wymaga przeciążenia MidPointRounding

http://msdn.microsoft.com/en-us/library/ms131274.aspx )

Jeśli otrzymujesz swoją wartość z bazy danych, rozważ rzutowanie zamiast konwersji: double value = (decimal) myRecord ["columnName"];

floren
źródło
0

Możesz po prostu ustawić jako:

decimal decNumber = 23.45600000m;
Console.WriteLine(decNumber.ToString("0.##"));
Danilo Ferreira
źródło
1
Aby to zadziałało, powinieneś umieścić dwadzieścia osiem znaków '#' zamiast tylko dwóch.
Jaime Hablutzel
tak, źle zrozumiałem jego pytanie ... = /
Danilo Ferreira,
0

Jeśli chcesz zachować liczbę dziesiętną, spróbuj następującego przykładu:

number = Math.Floor(number * 100000000) / 100000000;
phuongnd
źródło
0

Próba znalezienia bardziej przyjaznego rozwiązania DecimalToString ( https://stackoverflow.com/a/34486763/3852139 ):

private static decimal Trim(this decimal value)
{
    var s = value.ToString(CultureInfo.InvariantCulture);
    return s.Contains(CultureInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator)
        ? Decimal.Parse(s.TrimEnd('0'), CultureInfo.InvariantCulture)
        : value;
}

private static decimal? Trim(this decimal? value)
{
    return value.HasValue ? (decimal?) value.Value.Trim() : null;
}

private static void Main(string[] args)
{
    Console.WriteLine("=>{0}", 1.0000m.Trim());
    Console.WriteLine("=>{0}", 1.000000023000m.Trim());
    Console.WriteLine("=>{0}", ((decimal?) 1.000000023000m).Trim());
    Console.WriteLine("=>{0}", ((decimal?) null).Trim());
}

Wynik:

=>1
=>1.000000023
=>1.000000023
=>
Dmitri
źródło
-1

Bardzo prostą odpowiedzią jest użycie TrimEnd (). Oto wynik,

double value = 1.00;
string output = value.ToString().TrimEnd('0');

Wynik wynosi 1 Jeśli moja wartość to 1,01, to na wyjściu będzie 1,01

Raj De Inno
źródło
4
A co jeśli value = 100?
Stephen Drew
4
@Raj De Inno Nawet jeśli wartość wynosi 1,00, wyjście, które daje, wynosi „1”. nie „1”
Simba
-1

Poniższego kodu można użyć, aby nie używać typu ciągu:

int decimalResult = 789.500
while (decimalResult>0 && decimalResult % 10 == 0)
{
    decimalResult = decimalResult / 10;
}
return decimalResult;

Zwraca 789,5

Raul Minon
źródło
-2

Obcinanie końcowych zer jest bardzo łatwe, rozwiąż je za pomocą rzutu dwustronnego:

        decimal mydecimal = decimal.Parse("1,45000000"); //(I)
        decimal truncate = (decimal)(double)mydecimal;   //(II)

(I) -> Analizuj wartość dziesiętną z dowolnego źródła ciągu.

(II) -> Pierwsze: Rzutuj, aby podwoić to, aby usunąć końcowe zera. Po drugie: inne rzutowanie na dziesiętne, ponieważ nie istnieje niejawna konwersja z dziesiętnego na podwójny i odwrotnie)

amedriveroperez
źródło
5
Zakładasz, że wszystkie wartości, które mieszczą się w ułamku dziesiętnym, mieszczą się w podwójnym. Może to spowodować OverflowExeception. Możesz także stracić precyzję.
Martin Mulder,
-2

wypróbuj ten kod:

string value = "100";
value = value.Contains(".") ? value.TrimStart('0').TrimEnd('0').TrimEnd('.') : value.TrimStart('0');
Raju
źródło
Dlaczego dwie odpowiedzi, przyjacielu?
Raging Bull
2
A co z lokalizacjami, które nie używają znaku „.”?
Martin Mulder,
w ogóle nie jest opcją w porównaniu z innymi podejściami w tym poście. Konwersja na ciąg znaków iz powrotem jest zawsze złą opcją manipulowania liczbami (przynajmniej ze względu na lokalizację)
Vladimir Semashkin
-11

spróbuj w ten sposób

string s = "2.4200";

s = s.TrimStart('0').TrimEnd('0', '.');

a następnie przekonwertuj to na float

Singel
źródło
1
możesz użyć subString()do tego metody, ale zgodnie z zamieszczonym tutaj pytaniem nie widzę żadnego takiego wymagania =)
Singleton
2
A co z lokalizacjami, które nie używają znaku „.”
Dave Hillier
10
Nie udaje się to żałośnie dla liczb, które są parzystą wielokrotnością 10,0.
Ben Voigt,
1
To, co powiedział @BenVoigt, jest całkowicie poprawne, ponieważ przykład "12300.00"zostanie przycięty "123". Innym efektem, który wydaje się niepożądany, jest to, że niektóre liczby, takie jak, "0.00"są przycinane aż do pustego ciągu ""(chociaż ta liczba jest szczególnym przypadkiem bycia całkowitą „wielokrotnością 10,0”).
Jeppe Stig Nielsen