Najlepszy sposób wyświetlania liczb dziesiętnych bez końcowych zer

108

Czy istnieje program formatujący ekran, który wyprowadza liczby dziesiętne jako te reprezentacje ciągów w języku C # bez wykonywania żadnego zaokrąglania?

// decimal -> string

20 -> 20
20.00 -> 20
20.5 -> 20.5
20.5000 -> 20.5
20.125 -> 20.125
20.12500 -> 20.125
0.000 -> 0

{0. #} zaokrągli, a użycie niektórych funkcji przycinania nie będzie działać z powiązaną kolumną numeryczną w siatce.

JC.
źródło

Odpowiedzi:

147

Czy masz maksymalną liczbę miejsc dziesiętnych, które kiedykolwiek będziesz musiał wyświetlić? (Twoje przykłady mają maksymalnie 5).

Jeśli tak, myślę, że formatowanie za pomocą „0. #####” zrobi to, co chcesz.

    static void Main(string[] args)
    {
        var dList = new decimal[] { 20, 20.00m, 20.5m, 20.5000m, 20.125m, 20.12500m, 0.000m };

        foreach (var d in dList)
            Console.WriteLine(d.ToString("0.#####"));
    }
Toby
źródło
4
I zawsze możesz rzucić wygórowaną * liczbę symboli # w formacie. * nienaukowe
Anthony Pegram
3
+1 - nawiasem mówiąc, nie potrzebujesz wiodącego #. „0. #####” działa identycznie. I tak czy inaczej, to zaokrągla od zera (tylko FYI).
TrueWill
14
@TrueWill Właściwie to robi różnicę. d == 0,1m, a następnie "0. #" renderuje "0.1" i "#. #" renderuje ".1" bez wiodącego 0. Żadne z nich nie jest złe ani prawidłowe, zależy tylko od tego, czego chcesz.
AaronLS
4
Musiałem zawsze wyświetlać jedno miejsce dziesiętne, więc użyłem ciągu formatu "0.0#".
Kody z Hammerem
1
Zobacz odpowiedź Erwina Mayera poniżej.
shlgug
32

Właśnie nauczyłem się, jak prawidłowo używać specyfikatora Gformatu. Zobacz dokumentację MSDN . Trochę dalej znajduje się uwaga, która stwierdza, że ​​końcowe zera zostaną zachowane dla typów dziesiętnych, gdy nie określono dokładności. Dlaczego mieliby to zrobić, nie wiem, ale określenie maksymalnej liczby cyfr dla naszej dokładności powinno rozwiązać ten problem. Więc G29najlepiej jest formatować ułamki dziesiętne .

decimal test = 20.5000m;
test.ToString("G"); // outputs 20.5000 like the documentation says it should
test.ToString("G29"); // outputs 20.5 which is exactly what we want
Schmalls
źródło
21
Doprowadzi to do 0,00001 wyświetlanego jako 1E-05, choć
sehe
17

Ten format ciągu powinien stanowić Twój dzień: „0. ############################”. Pamiętaj jednak, że cyfry dziesiętne mogą mieć maksymalnie 29 cyfr znaczących.

Przykłady:

? (1000000.00000000000050000000000m).ToString("0.#############################")
-> 1000000.0000000000005

? (1000000.00000000000050000000001m).ToString("0.#############################")
-> 1000000.0000000000005

? (1000000.0000000000005000000001m).ToString("0.#############################")
-> 1000000.0000000000005000000001

? (9223372036854775807.0000000001m).ToString("0.#############################")
-> 9223372036854775807

? (9223372036854775807.000000001m).ToString("0.#############################")
-> 9223372036854775807.000000001
Erwin Mayer
źródło
4
To jest najbardziej ogólna odpowiedź na problem. Nie wiem, dlaczego nie jest to domyślny format „G” dla liczb dziesiętnych. Końcowe zera nie dodają żadnych informacji. Dokumentacja firmy Microsoft zawiera dziwne zastrzeżenie dotyczące liczby dziesiętnej: docs.microsoft.com/en-us/dotnet/standard/base-types/… Jeśli jednak liczba jest liczbą dziesiętną, a specyfikator precyzji jest pominięty, zawsze używana jest notacja ze stałym punktem a końcowe zera są zachowywane.
Eric Roller
10

To kolejna odmiana tego, co widziałem powyżej. W moim przypadku muszę zachować wszystkie znaczące cyfry po prawej stronie przecinka, co oznacza usunięcie wszystkich zer po najbardziej znaczącej cyfrze. Pomyślałem, że fajnie byłoby się nimi podzielić. Nie mogę jednak ręczyć za skuteczność tego, ale próbując osiągnąć estetykę, już jesteś skazany na nieefektywność.

public static string ToTrimmedString(this decimal target)
{
    string strValue = target.ToString(); //Get the stock string

    //If there is a decimal point present
    if (strValue.Contains("."))
    {
        //Remove all trailing zeros
        strValue = strValue.TrimEnd('0');

        //If all we are left with is a decimal point
        if (strValue.EndsWith(".")) //then remove it
            strValue = strValue.TrimEnd('.');
    }

    return strValue;
}

To wszystko, chciałem tylko dorzucić swoje dwa centy.

dyslexicanaboko
źródło
3
Jest to bez wątpienia najlepsze rozwiązanie, nie wybucha też na „0,0”. Dodałem domyślną flagę, aby liczby całkowite były opcjonalnie wyświetlane jako „1.0”, a to właśnie spowodowało, że wszystkie moje liczby dziesiętne znalazły się w wierszu. Nie mogę zrozumieć, dlaczego to nie jest najlepsza odpowiedź, + dużo.
Steve Hibbert
2
Jest to szczególnie dobre, jeśli ktoś już przekazał Ci ciąg znaków. Możesz po prostu zmienić to na ToTrimmedString (ten ciąg strValue) i usunąć pierwszą linię.
PRMan
Trafne spostrzeżenie. Myślę, że kiedy to pisałem, miałem do czynienia z wartościami, które były już ułamkami dziesiętnymi lub czymś w tym rodzaju, więc chciałem objąć całość. Zamiast tego możesz mieć treść tej metody wewnątrz innej przeciążonej metody, która zamiast tego akceptuje ciąg.
dyslexicanaboko
Jest to dobre rozwiązanie, gdy możesz wywołać ToTrimmedString (), ale istnieje wiele przypadków, w których jesteś ograniczony do ciągu formatu.
Mike Marynowski
1
Chociaż separator dziesiętny musi być specyficzny dla kultury, myślę, że jest to dobre rozwiązanie. Małe pytanie, dlaczego nie strValue.TrimEnd('0').TrimEnd('.')zamiast EndsWithczeku?
nawfal
5

Inne rozwiązanie, oparte na odpowiedzi dysleksikanaboko , ale niezależne od aktualnej kultury:

public static string ToTrimmedString(this decimal num)
{
    string str = num.ToString();
    string decimalSeparator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator;
    if (str.Contains(decimalSeparator))
    {
        str = str.TrimEnd('0');
        if(str.EndsWith(decimalSeparator))
        {
            str = str.RemoveFromEnd(1);
        }
    }
    return str;
}

public static string RemoveFromEnd(this string str, int characterCount)
{
    return str.Remove(str.Length - characterCount, characterCount);
}
David Dostal
źródło
Wspaniały. Z wyjątkiem SqlDecimal jest to CultureInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator pomimo ustawień regionalnych.
user2091150
4

Metoda przedłużenia:

public static class Extensions
{
    public static string TrimDouble(this string temp)
    {
        var value = temp.IndexOf('.') == -1 ? temp : temp.TrimEnd('.', '0');
        return value == string.Empty ? "0" : value;
    }
}

Przykładowy kod:

double[] dvalues = {20, 20.00, 20.5, 20.5000, 20.125, 20.125000, 0.000};
foreach (var value in dvalues)
    Console.WriteLine(string.Format("{0} --> {1}", value, value.ToString().TrimDouble()));

Console.WriteLine("==================");

string[] svalues = {"20", "20.00", "20.5", "20.5000", "20.125", "20.125000", "0.000"};
foreach (var value in svalues)
    Console.WriteLine(string.Format("{0} --> {1}", value, value.TrimDouble()));

Wynik:

20 --> 20
20 --> 20
20,5 --> 20,5
20,5 --> 20,5
20,125 --> 20,125
20,125 --> 20,125
0 --> 0
==================
20 --> 20
20.00 --> 2
20.5 --> 20.5
20.5000 --> 20.5
20.125 --> 20.125
20.125000 --> 20.125
0.000 --> 0
jgauffin
źródło
1
„20.00 -> 2” Myślę, że to nie jest dobre zachowanie. Aby to naprawić, wywołaj TrimEnd dwa razy: TrimEnd ('0'); TrimEnd (',');
Vadim
2
NIE UŻYWAJ kodu opisanego powyżej. @VadymK ma rację, że jego zachowanie jest wadliwe. Obetnie końcowe zera, które są PRZED kropką, jeśli nie ma kropki.
Sevin7
2
Odpowiedzi na stackoverflow nie są przeznaczone do kopiowania i wklejania. Są wykorzystywane jako źródło wiedzy . Ten sampel kodu uczy, IndexOfjak znajdować znaki w ciągu. Bardzo łatwo jest dostosować przykład, aby rozwiązać problem bez miejsc po przecinku.
jgauffin
2

Nie sądzę, że jest to możliwe po wyjęciu z pudełka, ale prosta metoda, taka jak ta, powinna to zrobić

public static string TrimDecimal(decimal value)
{
    string result = value.ToString(System.Globalization.CultureInfo.InvariantCulture);
    if (result.IndexOf('.') == -1)
        return result;

    return result.TrimEnd('0', '.');
}
Tim Skauge
źródło
2

Jest to dość łatwe do zrobienia po wyjęciu z pudełka:

Decimal YourValue; //just as example   
String YourString = YourValue.ToString().TrimEnd('0','.');

to usunie wszystkie końcowe zera z twojego Decimal.

Jedyne, co musisz zrobić, to dodać .ToString().TrimEnd('0','.');do zmiennej dziesiętnej, aby przekonwertować a DecimalnaString bez końcowych zer, jak w powyższym przykładzie.

W niektórych regionach powinien to być plik .ToString().TrimEnd('0',','); (gdzie zamiast kropki używają przecinka, ale dla pewności można też dodać kropkę i przecinek jako parametry).

(możesz również dodać oba jako parametry)

Mervin
źródło
Możesz nie zdawać sobie z tego sprawy, ale gdy paramsmasz do czynienia z argumentem, nie musisz jawnie konstruować tablicy. Więc zamiast rozwlekłych TrimEnd("0".ToCharArray())możesz po prostu napisać TrimEnd('0')(uwaga: przekazano pojedynczo charzamiast a char[]).
Dan Tao
@Dan Tao: Dzięki, zapomniałem o tym. Minęło trochę czasu, odkąd używałem C #. Chociaż obie wersje działają, zredagowałem swój post.
Mervin
@Mervin: Musisz podać a char, a nie a string(więc: pojedyncze cudzysłowy, a nie podwójne cudzysłowy). Naprawiłem to dla ciebie - mam nadzieję, że nie masz nic przeciwko.
Dan Tao
1
Twoje rozwiązanie wygeneruje wynik podobny do „2”. kiedy ma tylko zera.
jgauffin
2
Następnie sugerowałbym wywołanie .ToString (). TrimEnd ('0'). TrimEnd ('.'). Również zamiast „.” lepiej mieć CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator, aby działał w różnych kulturach.
Ε Г И І И О
1
decimal val = 0.000000000100m;
string result = val == 0 ? "0" : val.ToString().TrimEnd('0').TrimEnd('.');
Jerry Liang
źródło
0

Skończyło się na następującym kodzie:

    public static string DropTrailingZeros(string test)
    {
        if (test.Contains(CultureInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator))
        {
            test = test.TrimEnd('0');
        }

        if (test.EndsWith(CultureInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator))
        {
            test = test.Substring(0,
                test.Length - CultureInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator.Length);
        }

        return test;
    }
Arsen Zahray
źródło
0

Skończyło się na tym wariancie:

public static string Decimal2StringCompact(decimal value, int maxDigits)
    {
        if (maxDigits < 0) maxDigits = 0;
        else if (maxDigits > 28) maxDigits = 28;
        return Math.Round(value, maxDigits, MidpointRounding.ToEven).ToString("0.############################", CultureInfo.InvariantCulture);
    }

Zalety:

możesz określić maksymalną liczbę cyfr znaczących po punkcie do wyświetlenia w czasie wykonywania;

możesz jawnie określić metodę rundy;

możesz jawnie kontrolować kulturę.

lilo0
źródło
0

Możesz utworzyć metodę rozszerzenia

public static class ExtensionMethod {
  public static decimal simplify_decimal(this decimal value) => decimal.Parse($"{this:0.############}");
}
KevinBui
źródło
0

Poniższe metody rozszerzeń wykonałem dla siebie, aby pasowały do ​​jednego z moich projektów, ale może przyniosą korzyści komuś innemu.

using System.Numerics;
using System.Text.RegularExpressions;
internal static class ExtensionMethod
{
    internal static string TrimDecimal(this BigInteger obj) => obj.ToString().TrimDecimal();
    internal static string TrimDecimal(this decimal obj) => new BigInteger(obj).ToString().TrimDecimal();
    internal static string TrimDecimal(this double obj) => new BigInteger(obj).ToString().TrimDecimal();
    internal static string TrimDecimal(this float obj) => new BigInteger(obj).ToString().TrimDecimal();
    internal static string TrimDecimal(this string obj)
    {
        if (string.IsNullOrWhiteSpace(obj) || !Regex.IsMatch(obj, @"^(\d+([.]\d*)?|[.]\d*)$")) return string.Empty;
        Regex regex = new Regex("^[0]*(?<pre>([0-9]+)?)(?<post>([.][0-9]*)?)$");
        MatchEvaluator matchEvaluator = m => string.Concat(m.Groups["pre"].Length > 0 ? m.Groups["pre"].Value : "0", m.Groups["post"].Value.TrimEnd(new[] { '.', '0' }));
        return regex.Replace(obj, matchEvaluator);
    }
}

Chociaż będzie to wymagało odniesienia do System.Numerics.

Jeremy McCrorie
źródło