Supprimer les zéros de fin

177

J'ai des champs renvoyés par une collection comme

2.4200
2.0044
2.0000

Je veux des résultats comme

2.42
2.0044
2

J'ai essayé avec String.Format, mais il retourne 2.0000et le définit pour N0arrondir également les autres valeurs.

Zo a
la source
3
initialement le type d'enregistrement est une chaîne ??
Singleton
2
Voir ma réponse: String.Format()avec «G» devrait obtenir ce que vous voulez .. j'ai mis à jour ma réponse avec un lien vers les formats numériques standard. Très utile.
Dog Ears
3
Un décimal peut être converti en double, et la valeur ToStringpar défaut pour double émet les zéros de fin. lire ceci
Shimmy Weitzhandler
2
Et cela coûtera probablement moins de performances (pour un très grand nombre d'enregistrements) que de passer le "G" comme format de chaîne à la ToStringfonction.
Shimmy Weitzhandler du
4
Vous ne devriez pas convertir une décimale en double, cela perdra de son importance et pourrait introduire une puissance de deux inexactitudes d'arrondi.
kristianp

Réponses:

167

N'est-ce pas aussi simple que cela, si l'entrée EST une chaîne? Vous pouvez utiliser l'un de ceux-ci:

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

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

2.0m.ToString("G29")

Cela devrait fonctionner pour toutes les entrées.

Mise à jour Vérifiez les formats numériques standard que j'ai dû définir explicitement le spécificateur de précision sur 29, comme l'indique clairement la documentation:

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

Update Konrad a souligné dans les commentaires:

Méfiez-vous des valeurs comme 0,000001. Le format G29 les présentera de la manière la plus courte possible afin de passer à la notation exponentielle. string.Format("{0:G29}", decimal.Parse("0.00000001",System.Globalization.CultureInfo.GetCultureInfo("en-US")))donnera "1E-08" comme résultat.

Oreilles de chien
la source
1
De toutes les réponses, c'est la vôtre qui a impliqué le moins d'étapes. Merci Dog Ears.
Zo a
4
var d = 2,0 m; d.ToString ("G");
Dave Hillier
3
@Dave Hillier - Je vois, j'ai corrigé ma réponse. À votre santé. [ajout d'un "spécificateur de précision" explicite]
Dog Ears
16
Méfiez-vous des valeurs comme 0,000001. Le format G29 les présentera de la manière la plus courte possible afin de passer à la notation exponentielle. string.Format ("{0: G29}", decimal.Parse ("0.00000001", System.Globalization.CultureInfo.GetCultureInfo ("en-US"))) donnera "1E-08" comme résultat
Konrad
15
@Konrad - y a-t-il un moyen d'éviter la notation scientifique pour les nombres qui ont 5 ou 6 décimales?
Jill
202

J'ai rencontré le même problème mais dans un cas où je n'ai pas le contrôle de la sortie en chaîne, qui a été prise en charge par une bibliothèque. Après avoir examiné les détails de l'implémentation du type Decimal (voir http://msdn.microsoft.com/en-us/library/system.decimal.getbits.aspx ), j'ai trouvé une astuce intéressante (ici en extension méthode):

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

La partie exposant de la décimale est réduite à ce qui est nécessaire. L'appel de ToString () sur la décimale de sortie écrira le nombre sans aucun 0 à la fin. Par exemple

1.200m.Normalize().ToString();
Thomas Materna
la source
29
Cette réponse est propre parce que contrairement à pratiquement toutes les autres réponses à cette question (et même à tout le sujet), fonctionne réellement et fait ce que le PO demande.
Coxy
2
le nombre de 0 est-il ici une quantité exacte ou simplement pour couvrir la plupart des valeurs attendues?
Simon_Weaver
3
MSDN déclare "Le facteur d'échelle est implicitement le nombre 10, élevé à un exposant allant de 0 à 28", ce que je comprends comme le nombre décimal aura au plus 28 chiffres au-delà du point décimal. N'importe quel nombre de zéros> = 28 devrait fonctionner.
Thomas Materna
2
C'est la meilleure réponse! Le nombre dans la réponse a 34 chiffres, le 1suivi de 33 0-s, mais cela crée exactement la même decimalinstance qu'un nombre avec 29 chiffres, un 1et 28 0-s. Tout comme @ThomasMaterna l'a dit dans son commentaire. Non System.Decimalpeut avoir plus de 29 chiffres (les nombres avec les premiers chiffres supérieurs à 79228...ne peuvent avoir que 28 chiffres au total). Si vous voulez plus de zéros de fin, multipliez par 1.000...mau lieu de diviser. En outre, Math.Roundpeut couper certains zéros, par exemple Math.Round(27.40000m, 2)donne 27.40m, donc un seul zéro à gauche.
Jeppe Stig Nielsen
2
Excellent truc mais ne fonctionne PAS sur Mono et n'est pas garanti pour fonctionner sur les versions fonctionnelles de .NET
Bigjim
32

J'utilise ce code pour éviter la notation scientifique "G29":

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

EDIT: en utilisant le système 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
la source
1
C'est une merveilleuse réponse. C'est très simple et vous pouvez voir exactement ce qui se passe au lieu d'utiliser un formateur de chaîne magique.
Timothy Gonzalez
9
Bien, mais cela ne fonctionnera pas pour les cultures qui utilisent la virgule comme séparateur décimal. Vous auriez besoin d'utiliserCulture.NumberFormat.NumberDecimalSeparator
blearyeye
1
Meilleure approche. Tout le reste est inutilement compliqué. N'oubliez pas de toujours vérifier si votre nombre contient un point décimal.
RRM
1
Pour être une extension, il manque le "this" avant le paramètre: chaîne statique publique DecimalToString (cette décimale décimale)
João Antunes
Merci beaucoup pour les commentaires. J'ai mis à jour la réponse avec une nouvelle version avec les astuces.
x7BiT le
19

Utilisez le #symbole dièse ( ) pour n'afficher que les 0 à la fin lorsque cela est nécessaire. Voir les tests ci-dessous.

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
la source
7
Pas une réponse. Vous ne supprimez pas les zéros de fin, vous supprimez la précision. Votre méthode proposée num1.ToString("0.##")doit renvoyer 13.1534545765(aucun changement) car il n'y a pas de zéros à la fin.
Jan 'splite' K.
Et c'est exactement pourquoi j'affiche les réponses à mes trois entrées proposées afin que les utilisateurs puissent voir comment ma solution fonctionne.
Fizzix
Il ne répond pas à la question même si vous listez les entrées proposées.
Dave Friedel
2
Id upvote ceci 10 fois si je pouvais. Exactement ce dont j'avais besoin!
SubqueryCrunch
1
Réponse parfaite à la question. MEILLEURE réponse à mon avis. Le commentaire qu'il supprime la précision est correct, mais ce n'était pas la question. Le plus important - c'est ce dont j'ai besoin. Mes utilisateurs entreront généralement des nombres entiers, peut-être quelque chose comme 2,5, mais je dois prendre en charge 3 chiffres au-delà du point décimal. Je veux donc leur donner ce qu'ils sont entrés, pas avec un tas de zéros qu'ils n'ont pas entrés. par exemple, Console.Write ($ "num3 = {num3: 0. ##}"); => num3 = 30
bobwki
2

Une approche de très bas niveau, mais je pense que ce serait le moyen le plus performant en utilisant uniquement des calculs d'entiers rapides (et pas de méthodes d'analyse lente des chaînes et sensibles à la culture):

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
la source
1
J'avais essayé d'emprunter cette voie, mais je l'ai optimisée plus loin pour qu'elle soit vraiment beaucoup plus rapide que la vôtre, par exemple en ne divisant pas les entiers hi et mid (votre aet b) dans chaque itération while s'ils sont tous à zéro de toute façon. Cependant, j'ai trouvé cette solution étonnamment simple qui bat également facilement ce que vous et moi avons proposé en termes de performances .
Eugene Beresovsky
2

Cela fonctionnera:

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

Ou si votre valeur initiale est string:

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

Faites attention à ce commentaire .

Shimmy Weitzhandler
la source
2
@Damien, oui, et notez que pour ces raisons (vous ne ressentirez rien à moins de faire un milliard d'enregistrements), d'une part parce que le double est plus rapide, d'autre part, parce que passer un format de chaîne à la fonction ToString coûte plus de performances qu'autrement passer des paramètres. encore une fois, vous ne ressentirez rien à moins de travailler sur des milliards de disques.
Shimmy Weitzhandler
1
Vous n'avez pas à spécifier G, car double.ToStringpar défaut, les zéros de fin sont supprimés.
Shimmy Weitzhandler
4
Méfiez-vous des valeurs comme 0,000001. Ceux-ci seront présentés dans la notation exponentielle. double.Parse ("0.00000001", System.Globalization.CultureInfo.GetCultureInfo ("en-US")). ToString () donnera "1E-08" comme résultat
Konrad
17
Ne faites JAMAIS ça. Habituellement, la raison pour laquelle vous avez une décimale est que vous représentez un nombre avec précision (pas à peu près comme le fait un double). Certes, cette erreur en virgule flottante est probablement assez petite, mais pourrait entraîner l'affichage de nombres incorrects, néanmoins
Adam Tegen
2
Aïe, convertir un type de données 128 bits (décimal) en un type de données 64 bits (double) pour le formatage, n'est pas une bonne idée!
avl_sweden
2

C'est simple.

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

Testé.

À votre santé :)

Rakesh
la source
1

Cela dépend de ce que représente votre nombre et de la façon dont vous souhaitez gérer les valeurs: est-ce une devise, avez-vous besoin d'arrondi ou de troncature, avez-vous besoin de cet arrondi uniquement pour l'affichage?

Si pour l'affichage, pensez au formatage, les nombres sont x.ToString ("")

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

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

S'il s'agit simplement d'arrondi, utilisez une surcharge Math.Round qui nécessite une surcharge MidPointRounding

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

Si vous obtenez votre valeur à partir d'une base de données, envisagez la conversion au lieu de la conversion: double value = (decimal) myRecord ["columnName"];

florin
la source
0

Vous pouvez simplement définir comme:

decimal decNumber = 23.45600000m;
Console.WriteLine(decNumber.ToString("0.##"));
Danilo Ferreira
la source
1
Pour que cela fonctionne, vous devez mettre vingt-huit caractères «#» au lieu de seulement deux.
Jaime Hablutzel
ouais, j'ai mal compris sa question ... = /
Danilo Ferreira
0

Si vous souhaitez conserver le nombre décimal, essayez l'exemple suivant:

number = Math.Floor(number * 100000000) / 100000000;
phuongnd
la source
0

Essayer de faire une solution plus conviviale de 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());
}

Production:

=>1
=>1.000000023
=>1.000000023
=>
Dmitri
la source
-1

La réponse très simple consiste à utiliser TrimEnd (). Voici le résultat,

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

La sortie est 1 Si ma valeur est 1,01, ma sortie sera 1,01

Raj De Inno
la source
4
Et si value = 100?
Stephen Drew
4
@Raj De Inno Même si la valeur est de 1,00, la sortie qu'elle donne est "1." pas "1"
Simba
-1

Le code suivant peut être utilisé pour ne pas utiliser le type chaîne:

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

Renvoie 789,5

Raul Minon
la source
-2

Tronquer les zéros de fin est très simple, résolvez-les avec une conversion duplex:

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

(I) -> Analyser la valeur décimale de n'importe quelle source de chaîne.

(II) -> First: Cast pour doubler cette suppression des zéros de fin. Deuxièmement: Autre conversion en décimal car il n'existe pas de conversion implicite de décimal en double et vice versa)

amedriveroperez
la source
5
Vous supposez toutes les valeurs qui tiendraient dans une décimale, tenir dans un double. Cela peut provoquer une OverflowExeception. Vous pouvez également perdre de la précision.
Martin Mulder du
-2

essayez ce code:

string value = "100";
value = value.Contains(".") ? value.TrimStart('0').TrimEnd('0').TrimEnd('.') : value.TrimStart('0');
Raju
la source
Pourquoi deux réponses, mon ami?
Raging Bull
2
Qu'en est-il des locales qui n'utilisent pas «.»?
Martin Mulder du
pas une option du tout en comparaison avec d'autres approches dans ce poste. La conversion en chaîne et inversement est toujours une mauvaise option pour manipuler les nombres (au moins à cause des paramètres régionaux)
Vladimir Semashkin
-11

essayez comme ça

string s = "2.4200";

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

puis convertissez cela en float

Singleton
la source
1
vous pouvez utiliser la subString()méthode pour cela, mais selon la question postée ici, je ne vois aucune exigence comme celle-là =)
Singleton
2
Qu'en est-il des paramètres régionaux qui n'utilisent pas "."
Dave Hillier
10
Cela échoue lamentablement pour les nombres qui sont un multiple pair de 10,0.
Ben Voigt le
1
Ce que @BenVoigt a dit est tout à fait correct, car un exemple "12300.00"sera réduit "123". Un autre effet qui semble indésirable est que certains nombres, tels que, "0.00"sont coupés jusqu'à la chaîne vide ""(bien que ce nombre soit un cas particulier d'être un «multiple entier de 10,0»).
Jeppe Stig Nielsen