Il modo migliore per visualizzare i decimali senza zeri finali


107

Esiste un formattatore di visualizzazione che restituirà i decimali come queste rappresentazioni di stringa in c # senza eseguire alcun arrotondamento?

// 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. #} verrà arrotondato e l'utilizzo di alcune funzioni di tipo Trim non funzionerà con una colonna numerica limitata in una griglia.

Risposte:


147

Hai un numero massimo di cifre decimali che dovrai mai visualizzare? (I tuoi esempi hanno un massimo di 5).

Se è così, penserei che la formattazione con "0. #####" farebbe quello che vuoi.

    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.#####"));
    }

4
E puoi sempre lanciare un numero esorbitante * di simboli # nel formato. * non scientifico
Anthony Pegram,

3
+1 - per inciso, non è necessario il # iniziale. "0. #####" funziona in modo identico. E in ogni caso, questo arrotonda da zero (solo FYI).
TrueWill

14
@ TrueWill In realtà fa la differenza. d == 0.1m poi "0. #" restituisce "0.1" e "#. #" restituisce ".1" senza lo zero iniziale. Né è giusto né sbagliato, dipende solo da ciò che si desidera.
AaronLS

4
Avevo bisogno di visualizzare sempre una cifra decimale, quindi ho usato la stringa di formato "0.0#".
Codici con Hammer

1
Vedi la risposta di Erwin Mayer di seguito.
shlgug

32

Ho appena imparato a utilizzare correttamente l' Gidentificatore di formato. Vedere la documentazione di MSDN . C'è una nota un po 'più in basso che afferma che gli zeri finali verranno conservati per i tipi decimali quando non viene specificata alcuna precisione. Perché lo farebbero non lo so, ma specificare il numero massimo di cifre per la nostra precisione dovrebbe risolvere il problema. Quindi, per la formattazione dei decimali, G29è la soluzione migliore.

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

21
Questo porterà a 0,00001 visualizzato come 1E-05 anche se
sehe

17

Questo formato di stringa dovrebbe rendere la tua giornata: "0. ############################". Tieni presente che i decimali possono contenere al massimo 29 cifre significative.

Esempi:

? (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

4
Questa è la risposta più generale al problema. Non sono sicuro del motivo per cui questo non è il formato "G" predefinito per i decimali. Gli zeri finali non aggiungono alcuna informazione. La documentazione Microsoft ha uno strano avvertimento per i decimali: docs.microsoft.com/en-us/dotnet/standard/base-types/… Tuttavia, se il numero è un decimale e lo specificatore di precisione viene omesso, viene sempre utilizzata la notazione a virgola fissa e gli zeri finali vengono conservati.
Eric Roller

10

Questa è ancora un'altra variazione di ciò che ho visto sopra. Nel mio caso ho bisogno di preservare tutte le cifre significative a destra del punto decimale, il che significa eliminare tutti gli zeri dopo la cifra più significativa. Ho solo pensato che sarebbe stato bello condividere. Non posso garantire l'efficacia di questo, però, ma quando cerchi di ottenere l'estetica, sei già praticamente dannato alle inefficienze.

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;
}

Questo è tutto, volevo solo buttare i miei due centesimi.


3
Questa è facilmente la soluzione migliore, non esplode neanche per "0,0". Ho aggiunto un flag predefinito per far sì che gli interi mostrino opzionalmente come "1.0", e questo ha appena allineato tutti i miei decimali. Non riesco a capire perché questa non sia la risposta migliore, + lotti.
Steve Hibbert,

2
Ciò è particolarmente utile se qualcuno ti ha già passato una stringa. Puoi semplicemente cambiarlo in ToTrimmedString (questa stringa strValue) e rimuovere la prima riga.
PRMan

È un buon punto. Penso che quando stavo scrivendo questo avevo a che fare con valori che erano già decimali o qualcosa del genere, quindi volevo comprendere l'intera cosa. Potresti invece avere il corpo di quel metodo all'interno di un altro metodo sovraccarico che accetta invece una stringa.
dyslexicanaboko

È una buona soluzione quando puoi chiamare ToTrimmedString () ma ci sono molti casi in cui sei limitato a una stringa di formato.
Mike Marynowski

1
Sebbene il separatore decimale debba essere specifico della cultura, penso che questa sia una buona soluzione. Una piccola domanda, perché non solo al strValue.TrimEnd('0').TrimEnd('.')posto dell'assegno EndsWith?
nawfal

5

Un'altra soluzione, basata sulla risposta di dyslexicanaboko , ma indipendente dalla cultura attuale:

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);
}

Grande. Ad eccezione di SqlDecimal, è CultureInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator nonostante le impostazioni internazionali.
user2091150

4

Metodo di estensione:

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;
    }
}

Codice di esempio:

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()));

Produzione:

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

1
"20.00 -> 2" Penso che non sia un buon comportamento. Per risolverlo, chiama TrimEnd due volte: TrimEnd ('0'); TrimEnd ( '');
Vadim

2
NON USARE il codice scritto sopra. @VadymK ha ragione sul fatto che il suo comportamento è imperfetto. Taglia gli zeri finali che sono PRIMA del periodo se non c'è punto.
Sevin7

2
Le risposte su stackoverflow non sono per copia-incolla. Sono usati come fonte di apprendimento . Questo codice di esempio ti insegna a usare IndexOfper trovare i caratteri all'interno di una stringa. È abbastanza facile regolare l'esempio per risolvere il problema senza decimali.
jgauffin

2

Non penso che sia possibile fuori dagli schemi, ma un metodo semplice come questo dovrebbe farlo

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

    return result.TrimEnd('0', '.');
}

2

È abbastanza facile da fare fuori dagli schemi:

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

che rimuoverà tutti gli zeri finali dal tuo decimale.

L'unica cosa che devi fare è aggiungere .ToString().TrimEnd('0','.');a una variabile decimale per convertire a Decimalin a Stringsenza zeri finali, come nell'esempio sopra.

In alcune regioni dovrebbe essere un file .ToString().TrimEnd('0',','); (dove usano una virgola invece di un punto, ma puoi anche aggiungere un punto e una virgola come parametri per essere sicuro).

(puoi anche aggiungere entrambi come parametri)


Potresti non rendertene conto, ma quando paramshai a che fare con un argomento, non devi costruire esplicitamente un array. Quindi invece del verboso TrimEnd("0".ToCharArray())potresti semplicemente scrivere TrimEnd('0')(nota: passato singolo charinvece di a char[]).
Dan Tao,

@ Dan Tao: Grazie, l'ho dimenticato. È passato un po 'di tempo da quando ho usato C #. Sebbene entrambe le versioni funzionino, ho modificato il mio post.
Mervin

@Mervin: Devi passare a char, non a string(quindi: virgolette singole, non virgolette doppie). L'ho aggiustato per te - spero non ti dispiaccia.
Dan Tao,

1
La tua soluzione produrrà output come "2". quando ha solo zeri.
jgauffin

2
Quindi suggerirei di chiamare .ToString (). TrimEnd ('0'). TrimEnd ('.'). Inoltre invece di "." è meglio avere CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator in modo che funzioni tra le culture.
Ε Г И І И О

1
decimal val = 0.000000000100m;
string result = val == 0 ? "0" : val.ToString().TrimEnd('0').TrimEnd('.');

0

Ho finito con il seguente codice:

    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;
    }

0

Ho finito con questa variante:

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);
    }

vantaggi:

è possibile specificare il numero massimo di cifre significative dopo il punto da visualizzare in fase di esecuzione;

puoi specificare esplicitamente un metodo round;

puoi controllare esplicitamente una cultura.


0

Puoi creare un metodo di estensione

public static class ExtensionMethod {
  public static decimal simplify_decimal(this decimal value) => decimal.Parse($"{this:0.############}");
}

0

Ho creato i seguenti metodi di estensione per me stesso per adattarli a uno dei miei progetti, ma forse saranno utili per qualcun altro.

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);
    }
}

Sebbene richiederà un riferimento a System.Numerics.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.