Der beste Weg, um Dezimalstellen ohne nachgestellte Nullen anzuzeigen

107

Gibt es einen Anzeigeformatierer, der Dezimalstellen als diese Zeichenfolgendarstellungen in c # ausgibt, ohne eine Rundung vorzunehmen?

// 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. #} wird gerundet, und die Verwendung einer Funktion zum Zuschneiden funktioniert nicht mit einer gebundenen numerischen Spalte in einem Raster.

JC.
quelle

Antworten:

147

Haben Sie eine maximale Anzahl von Dezimalstellen, die Sie jemals anzeigen müssen? (Ihre Beispiele haben maximal 5).

Wenn ja, würde ich denken, dass die Formatierung mit "0. #####" tun würde, was Sie wollen.

    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
quelle
4
Und Sie können immer eine exorbitante * Anzahl von # Symbolen in das Format werfen. * nicht wissenschaftlich
Anthony Pegram
3
+1 - du brauchst übrigens nicht die führende #. "0. #####" funktioniert identisch. Und so oder so rundet dies von Null ab (nur zu Ihrer Information).
TrueWill
14
@TrueWill Eigentlich macht es einen Unterschied. d == 0.1m dann "0. #" rendert "0.1" und "#. #" rendert ".1" ohne die führende 0. Weder ist falsch noch richtig, hängt nur davon ab, was Sie wollen.
AaronLS
4
Ich musste immer eine Dezimalstelle anzeigen, also habe ich die Formatzeichenfolge verwendet "0.0#".
Codes mit Hammer
1
Siehe Erwin Mayers Antwort unten.
Shlgug
32

Ich habe gerade gelernt, wie man den GFormatbezeichner richtig verwendet . Siehe die MSDN-Dokumentation . Etwas weiter unten befindet sich ein Hinweis, der besagt, dass nachgestellte Nullen für Dezimaltypen beibehalten werden, wenn keine Genauigkeit angegeben ist. Warum sie dies tun würden, weiß ich nicht, aber die Angabe der maximalen Anzahl von Stellen für unsere Genauigkeit sollte dieses Problem beheben. Für die Formatierung von Dezimalstellen G29ist dies die beste Wahl.

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
quelle
21
Dies führt jedoch dazu, dass 0,00001 als 1E-05 angezeigt wird
siehe
17

Dieses Zeichenfolgenformat sollte Ihren Tag bestimmen: "0. ###############################". Beachten Sie jedoch, dass Dezimalstellen höchstens 29 signifikante Stellen haben können.

Beispiele:

? (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
quelle
4
Dies ist die allgemeinste Antwort auf das Problem. Ich bin mir nicht sicher, warum dies nicht das Standardformat "G" für Dezimalzahlen ist. Die nachfolgenden Nullen fügen keine Informationen hinzu. In der Microsoft-Dokumentation gibt es eine merkwürdige Einschränkung für die Dezimalzahl: docs.microsoft.com/en-us/dotnet/standard/base-types/… Wenn die Zahl jedoch eine Dezimalzahl ist und der Präzisionsspezifizierer weggelassen wird, wird immer die Festkommanotation verwendet und nachfolgende Nullen bleiben erhalten.
Eric Roller
10

Dies ist eine weitere Variation dessen, was ich oben gesehen habe. In meinem Fall muss ich alle signifikanten Ziffern rechts vom Dezimalpunkt beibehalten, dh alle Nullen nach der höchstwertigen Ziffer löschen. Ich dachte nur, es wäre schön zu teilen. Ich kann zwar nicht für die Effizienz bürgen, aber wenn Sie versuchen, Ästhetik zu erreichen, sind Sie bereits zu Ineffizienzen verdammt.

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

Das ist alles, ich wollte nur meine zwei Cent einwerfen.

Legasthenikeraboko
quelle
3
Dies ist einfach die beste Lösung, sie sprengt auch nicht für "0.0". Ich habe ein Standardflag hinzugefügt, damit Ganzzahlen optional als "1.0" angezeigt werden. Dadurch sind alle meine Dezimalstellen in Einklang gebracht worden. Kann nicht verstehen, warum dies nicht die beste Antwort ist, + viele.
Steve Hibbert
2
Dies ist besonders gut, wenn Ihnen bereits jemand eine Zeichenfolge übergeben hat. Sie können es einfach in ToTrimmedString (diese Zeichenfolge strValue) ändern und die erste Zeile entfernen.
PRMan
Das ist ein guter Punkt. Ich denke, als ich das schrieb, hatte ich es mit Werten zu tun, die bereits Dezimalstellen waren oder so, also wollte ich das Ganze erfassen. Sie könnten stattdessen den Hauptteil dieser Methode in einer anderen überladenen Methode haben, die stattdessen eine Zeichenfolge akzeptiert.
Legasthenikeraboko
Es ist eine gute Lösung, wenn Sie ToTrimmedString () aufrufen können, aber es gibt viele Fälle, in denen Sie auf eine Formatzeichenfolge beschränkt sind.
Mike Marynowski
1
Während das Dezimaltrennzeichen kulturspezifisch sein muss, halte ich dies für eine gute Lösung. Eine kleine Frage, warum nicht einfach strValue.TrimEnd('0').TrimEnd('.')statt des EndsWithSchecks?
Nawfal
5

Eine andere Lösung, die auf der Antwort von Legastheniker basiert , jedoch unabhängig von der aktuellen Kultur ist:

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
quelle
Toll. Mit Ausnahme von SqlDecimal handelt es sich trotz regionaler Einstellungen um CultureInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator.
user2091150
4

Verlängerungsmethode:

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

Beispielcode:

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

Ausgabe:

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
quelle
1
"20.00 -> 2" Ich denke, es ist kein gutes Verhalten. Um dies zu beheben, rufen Sie TrimEnd zweimal auf: TrimEnd ('0'); TrimEnd (',');
Vadim
2
VERWENDEN SIE NICHT den oben beschriebenen Code. @VadymK ist richtig, dass sein Verhalten fehlerhaft ist. Es werden nachfolgende Nullen abgeschnitten, die VOR der Periode liegen, wenn keine Periode vorhanden ist.
Sevin7
2
Antworten zum Stackoverflow gelten nicht zum Kopieren und Einfügen. Sie werden als Lernquelle verwendet . Mit diesem Sampel-Code lernen Sie, IndexOfZeichen innerhalb einer Zeichenfolge zu finden. Es ist ziemlich einfach, das Beispiel anzupassen, um das Problem ohne Dezimalstellen zu lösen.
Jgauffin
2

Ich denke nicht, dass es sofort möglich ist, aber eine einfache Methode wie diese sollte es tun

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
quelle
2

Es ist ganz einfach, sofort loszulegen:

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

Dadurch werden alle nachgestellten Nullen aus Ihrer Dezimalzahl entfernt.

Das einzige, was Sie tun müssen, ist das Hinzufügen .ToString().TrimEnd('0','.');zu einer Dezimalvariablen, um a Decimalin eine Stringohne nachgestellte Nullen umzuwandeln , wie im obigen Beispiel.

In einigen Regionen sollte es ein sein .ToString().TrimEnd('0',',');(wo sie ein Komma anstelle eines Punktes verwenden, aber Sie können auch einen Punkt und ein Komma als Parameter hinzufügen, um sicherzugehen).

(Sie können auch beide als Parameter hinzufügen)

Mervin
quelle
Möglicherweise erkennen paramsSie dies nicht , aber wenn Sie sich mit einem Argument befassen , müssen Sie kein Array explizit erstellen. Anstelle des Ausführlichen können TrimEnd("0".ToCharArray())Sie also einfach schreiben TrimEnd('0')(Hinweis: Single charstatt a übergeben char[]).
Dan Tao
@ Dan Tao: Danke, das habe ich vergessen. Es ist schon eine Weile her, seit ich C # verwendet habe. Obwohl beide Versionen funktionieren, habe ich meinen Beitrag bearbeitet.
Mervin
@Mervin: Sie müssen ein char, kein a übergeben string(also: einfache Anführungszeichen, keine doppelten Anführungszeichen). Ich habe es für dich repariert - hoffentlich macht es dir nichts aus.
Dan Tao
1
Ihre Lösung erzeugt eine Ausgabe wie "2". wenn es nur Nullen bekam.
Jgauffin
2
Dann würde ich vorschlagen, .ToString (). TrimEnd ('0'). TrimEnd ('.') Aufzurufen. Auch anstelle von '.' Es ist besser, CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator zu haben, damit es kulturübergreifend funktioniert.
О Г И І І О
1
decimal val = 0.000000000100m;
string result = val == 0 ? "0" : val.ToString().TrimEnd('0').TrimEnd('.');
Jerry Liang
quelle
0

Am Ende hatte ich folgenden Code:

    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
quelle
0

Ich bin mit dieser Variante gelandet:

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

Vorteile:

Sie können die maximale Anzahl von signifikanten Stellen nach dem Punkt angeben, der zur Laufzeit angezeigt werden soll.

Sie können explizit eine runde Methode angeben.

Sie können eine Kultur explizit steuern.

lilo0
quelle
0

Sie können eine Erweiterungsmethode erstellen

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

Ich habe die folgenden Erweiterungsmethoden für mich erstellt, um sie an eines meiner Projekte anzupassen, aber vielleicht sind sie für andere von Vorteil.

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

Es wird jedoch ein Verweis auf erforderlich sein System.Numerics.

Jeremy McCrorie
quelle