Meilleur moyen d'afficher les décimales sans zéros de fin


107

Existe-t-il un formateur d'affichage qui produira des décimales comme ces représentations de chaîne en c # sans faire aucun arrondi?

// 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. #} arrondira et l'utilisation d'une fonction de type Trim ne fonctionnera pas avec une colonne numérique liée dans une grille.

Réponses:


147

Avez-vous un nombre maximum de décimales à afficher? (Vos exemples ont un maximum de 5).

Si tel est le cas, je pense que le formatage avec "0. #####" ferait ce que vous voulez.

    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
Et vous pouvez toujours lancer un nombre exorbitant * de symboles # au format. * not Scientific
Anthony Pegram

3
+1 - d'ailleurs, vous n'avez pas besoin du premier #. "0. #####" fonctionne de la même manière. Et de toute façon, cela arrondit à zéro (juste pour info).
TrueWill

14
@TrueWill En fait, cela fait une différence. d == 0.1m puis "0. #" rend "0.1" et "#. #" rend ".1" sans le 0 initial. Ni l'un ni l'autre n'est faux, cela dépend juste de ce que vous voulez.
AaronLS

4
J'avais besoin d'afficher une décimale à tout moment, j'ai donc utilisé la chaîne de format "0.0#".
Codes avec Hammer

1
Voir la réponse d'Erwin Mayer ci-dessous.
shlgug

32

Je viens d'apprendre à utiliser correctement le Gspécificateur de format. Consultez la documentation MSDN . Il y a une note un peu plus bas qui indique que les zéros de fin seront conservés pour les types décimaux lorsqu'aucune précision n'est spécifiée. Pourquoi ils feraient cela, je ne sais pas, mais spécifier le nombre maximum de chiffres pour notre précision devrait résoudre ce problème. Donc, pour le formatage des décimales, G29c'est le meilleur pari.

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
Cela conduira à 0,00001 affiché 1E-05 si
sehe

17

Ce format de chaîne devrait rendre votre journée: "0. ###########################". Gardez à l'esprit que les décimales peuvent avoir au plus 29 chiffres significatifs.

Exemples:

? (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
C'est la réponse la plus générale au problème. Je ne sais pas pourquoi ce n'est pas le format "G" par défaut pour les décimales. Les zéros de fin n'ajoutent aucune information. La documentation Microsoft a une étrange mise en garde pour les décimales: docs.microsoft.com/en-us/dotnet/standard/base-types / ... Cependant, si le nombre est un décimal et que le spécificateur de précision est omis, la notation à virgule fixe est toujours utilisée et les zéros de fin sont conservés.
Eric Roller

10

C'est encore une autre variation de ce que j'ai vu ci-dessus. Dans mon cas, je dois conserver tous les chiffres significatifs à droite de la virgule décimale, ce qui signifie supprimer tous les zéros après le chiffre le plus significatif. Je pensais juste que ce serait bien de partager. Je ne peux pas garantir l'efficacité de cela, mais lorsque vous essayez d'atteindre l'esthétique, vous êtes déjà condamné à des inefficacités.

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

C'est tout, je voulais juste ajouter mes deux cents.


3
C'est de loin la meilleure solution, elle n'explose pas non plus pour "0.0". J'ai ajouté un indicateur par défaut pour que les entiers s'affichent éventuellement sous la forme "1.0", et cela vient de mettre toutes mes décimales en ligne. Je ne comprends pas pourquoi ce n'est pas la meilleure réponse, + beaucoup.
Steve Hibbert

2
Ceci est particulièrement utile si quelqu'un vous a déjà passé une chaîne. Vous pouvez simplement le changer en ToTrimmedString (cette chaîne strValue) et supprimer la première ligne.
PRMan

C'est un bon point. Je pense que lorsque j'écrivais cela, je parlais de valeurs qui étaient déjà des décimales ou quelque chose du genre, alors je voulais englober le tout. Vous pourriez à la place avoir le corps de cette méthode dans une autre méthode surchargée qui accepte une chaîne à la place.
dyslexicanaboko

C'est une bonne solution lorsque vous pouvez appeler ToTrimmedString () mais dans de nombreux cas, vous êtes limité à une chaîne de format.
Mike Marynowski

1
Bien que le séparateur décimal doive être spécifique à la culture, je pense que c'est une bonne solution. Une petite question, pourquoi pas juste à la strValue.TrimEnd('0').TrimEnd('.')place du EndsWithchèque?
nawfal

5

Autre solution, basée sur la réponse de dyslexicanaboko, mais indépendante de la culture actuelle:

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

Génial. À l'exception de SqlDecimal, il s'agit de CultureInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator malgré les paramètres régionaux.
user2091150

4

Méthode d'extension:

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

Exemple de code:

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

Production:

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" Je pense que ce n'est pas un bon comportement. Pour le réparer, appelez TrimEnd deux fois: TrimEnd ('0'); TrimEnd (',');
Vadim

2
N'UTILISEZ PAS le code écrit ci-dessus. @VadymK a raison de dire que son comportement est défectueux. Il coupera les zéros de fin qui sont AVANT le point s'il n'y a pas de point.
Sevin7

2
Les réponses sur stackoverflow ne sont pas pour le copier-coller. Ils sont utilisés comme source d' apprentissage . Ce code sampel vous apprend à utiliser IndexOfpour rechercher des caractères dans une chaîne. Il est assez facile d'ajuster l'exemple pour résoudre le problème sans décimales.
jgauffin

2

Je ne pense pas que ce soit possible prêt à l'emploi, mais une méthode simple comme celle-ci devrait le faire

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

C'est assez facile à faire hors de la boîte:

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

cela supprimera tous les zéros de fin de votre décimal.

La seule chose que vous devez faire est d'ajouter .ToString().TrimEnd('0','.');à une variable décimale pour convertir a Decimalen a Stringsans zéros de fin, comme dans l'exemple ci-dessus.

Dans certaines régions, cela devrait être un .ToString().TrimEnd('0',',');(où ils utilisent une virgule au lieu d'un point, mais vous pouvez également ajouter un point et une virgule comme paramètres pour être sûr).

(vous pouvez également ajouter les deux comme paramètres)


Vous ne vous en rendez peut-être pas compte, mais lorsque vous traitez un paramsargument, vous n'avez pas à construire un tableau explicitement. Donc, au lieu du verbeux, TrimEnd("0".ToCharArray())vous pouvez simplement écrire TrimEnd('0')(note: passé simple charau lieu de a char[]).
Dan Tao

@Dan Tao: Merci, j'ai oublié ça. Cela fait un moment que j'utilise C #. Bien que les deux versions fonctionnent, j'ai modifié mon message.
Mervin le

@Mervin: Vous devez passer un char, pas un string(donc: des guillemets simples, pas des guillemets doubles). Je l'ai réparé pour vous - j'espère que cela ne vous dérange pas.
Dan Tao

1
Votre solution produira une sortie comme "2." quand il n'y a que des zéros.
jgauffin

2
Ensuite, je suggérerais d'appeler .ToString (). TrimEnd ('0'). TrimEnd ('.'). Aussi au lieu de «.» il est préférable d'avoir CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator afin qu'il fonctionne à travers les cultures.
Ε Г И І И О

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

0

J'ai fini avec le code suivant:

    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

J'ai fini avec cette 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);
    }

Avantages:

vous pouvez spécifier le nombre maximum de chiffres significatifs après le point à afficher au moment de l'exécution;

vous pouvez spécifier explicitement une méthode ronde;

vous pouvez contrôler explicitement une culture.


0

Vous pouvez créer une méthode d'extension

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

0

J'ai créé les méthodes d'extension ci-dessous pour moi-même pour s'adapter à l'un de mes projets, mais elles seront peut-être bénéfiques à quelqu'un d'autre.

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

Bien qu'il faudra une référence à System.Numerics.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.