Melhor maneira de exibir decimal sem zeros à direita

107

Existe um formatador de exibição que produzirá decimais como essas representações de string em c # sem fazer nenhum arredondamento?

// 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. #} irá arredondar e usar alguma função do tipo Trim não funcionará com uma coluna numérica ligada em uma grade.

JC.
fonte

Respostas:

147

Você tem um número máximo de casas decimais que precisa exibir? (Seus exemplos têm no máximo 5).

Nesse caso, acho que a formatação com "0. #####" faria o que você deseja.

    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
fonte
4
E você sempre pode lançar um número exorbitante * de símbolos # no formato. * não científico
Anthony Pegram
3
+1 - aliás, você não precisa do # principal. "0. #####" funciona de forma idêntica. E de qualquer maneira, isso arredonda a partir de zero (apenas para sua informação).
TrueWill
14
@TrueWill Na verdade, faz diferença. d == 0.1m então "0. #" renderiza "0.1" e "#. #" renderiza ".1" sem o 0. Nenhum dos dois está certo ou errado, apenas depende do que você deseja.
AaronLS
4
Eu precisava exibir uma casa decimal em todos os momentos, então usei a string de formato "0.0#".
Códigos com Martelo de
1
Veja a resposta de Erwin Mayer abaixo.
shlgug
32

Acabei de aprender como usar corretamente o Gespecificador de formato. Consulte a documentação do MSDN . Há uma nota um pouco abaixo que afirma que os zeros finais serão preservados para tipos decimais quando nenhuma precisão for especificada. Por que eles fariam isso, eu não sei, mas especificar o número máximo de dígitos para nossa precisão deve resolver o problema. Portanto, para formatar decimais, G29é a melhor aposta.

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
fonte
21
No entanto, isso levará a 0,00001 exibido como 1E-05
ver
17

Este formato de string deve tornar o seu dia: "0. ##################################". Lembre-se de que os decimais podem ter no máximo 29 dígitos significativos.

Exemplos:

? (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
fonte
4
Esta é a resposta mais geral para o problema. Não tenho certeza por que este não é o formato "G" padrão para decimal. Os zeros à direita não adicionam nenhuma informação. A documentação da Microsoft tem uma advertência estranha para decimal: docs.microsoft.com/en-us/dotnet/standard/base-types/… No entanto, se o número for um decimal e o especificador de precisão for omitido, a notação de ponto fixo será sempre usada e os zeros finais são preservados.
Eric Roller,
10

Esta é mais uma variação do que vi acima. No meu caso, preciso preservar todos os dígitos significativos à direita da vírgula decimal, o que significa eliminar todos os zeros após o dígito mais significativo. Achei que seria bom compartilhar. Eu não posso garantir a eficiência disso, mas quando você tenta atingir a estética, você já está condenado às ineficiências.

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

Isso é tudo, só queria jogar meus dois centavos.

dislexicanaboko
fonte
3
Esta é facilmente a melhor solução, também não explode para "0,0". Eu adicionei um sinalizador padrão para fazer os inteiros opcionalmente mostrarem como "1.0", e isso apenas fez todos os meus decimais ficarem alinhados. Não consigo entender porque esta não é a melhor resposta, + lotes.
Steve Hibbert
2
Isso é especialmente bom se alguém já lhe passou uma string. Você pode apenas alterá-lo para ToTrimmedString (esta string strValue) e remover a primeira linha.
PRMan
Este é um bom ponto. Acho que quando eu estava escrevendo isso, estava lidando com valores que já eram decimais ou algo assim, então eu queria englobar a coisa toda. Em vez disso, você poderia ter o corpo desse método dentro de outro método sobrecarregado que aceita uma string.
dyslexicanaboko
É uma boa solução quando você pode chamar ToTrimmedString (), mas há muitos casos em que você está limitado a uma string de formato.
Mike Marynowski
1
Embora o separador decimal deva ser específico da cultura, acho que esta é uma boa solução. Uma pequena pergunta, por que não apenas em strValue.TrimEnd('0').TrimEnd('.')vez do EndsWithcheque?
nawfal
5

Outra solução, baseada na resposta do dislexicanaboko , mas independente da cultura atual:

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
fonte
Ótimo. Exceto para SqlDecimal, é CultureInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator, apesar das configurações regionais.
user2091150
4

Método de extensão:

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

Código de exemplo:

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

Resultado:

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
fonte
1
"20,00 -> 2" Acho que não é um bom comportamento. Para corrigir, chame TrimEnd duas vezes: TrimEnd ('0'); TrimEnd (',');
Vadim
2
NÃO USE o código escrito acima. @VadymK está correto ao dizer que seu comportamento é falho. Ele cortará os zeros à direita que estão ANTES do ponto final, se não houver ponto final.
Sevin7
2
Respostas no stackoverflow não são para copiar e colar. Eles são usados ​​como fonte de aprendizado . Este código sampel ensina a usar IndexOfpara encontrar caracteres dentro de uma string. É muito fácil ajustar o exemplo para resolver o problema sem decimais.
jgauffin
2

Não acho que seja possível out-of-the-box, mas um método simples como este deve servir

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

É muito fácil de fazer fora da caixa:

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

isso removerá todos os zeros finais de seu decimal.

A única coisa que você precisa fazer é adicionar .ToString().TrimEnd('0','.');a uma variável decimal para converter a Decimalem a Stringsem zeros à direita, como no exemplo acima.

Em algumas regiões, deve ser um .ToString().TrimEnd('0',','); (onde eles usam uma vírgula em vez de um ponto, mas você também pode adicionar um ponto e uma vírgula como parâmetros para ter certeza).

(você também pode adicionar ambos como parâmetros)

Mervin
fonte
Você pode não perceber isso, mas ao lidar com um paramsargumento, não é necessário construir uma matriz explicitamente. Portanto, em vez de verboso, TrimEnd("0".ToCharArray())você pode apenas escrever TrimEnd('0')(nota: passou como único em charvez de a char[]).
Dan Tao
@Dan Tao: Obrigado, esqueci isso. Já faz um tempo que não uso o C #. Embora ambas as versões funcionem, eu editei meu post.
Mervin
@Mervin: Você precisa passar um char, não um string(então: aspas simples, não aspas duplas). Eu consertei para você - espero que você não se importe.
Dan Tao
1
Sua solução produzirá uma saída como "2". quando só obteve zeros.
jgauffin
2
Em seguida, sugiro chamar .ToString (). TrimEnd ('0'). TrimEnd ('.'). Também em vez de '.' é melhor ter CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator para que funcione em várias culturas.
Ε Г И І И О
1
decimal val = 0.000000000100m;
string result = val == 0 ? "0" : val.ToString().TrimEnd('0').TrimEnd('.');
Jerry Liang
fonte
0

Acabei com o seguinte código:

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

Acabei com esta 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);
    }

Vantagens:

você pode especificar o número máximo de dígitos significativos após o ponto a ser exibido no tempo de execução;

você pode especificar explicitamente um método round;

você pode controlar explicitamente uma cultura.

lilo0
fonte
0

Você pode criar um método de extensão

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

Eu fiz os métodos de extensão abaixo para mim mesmo para caber em um dos meus projetos, mas talvez eles sejam benéficos para outra pessoa.

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

Embora exija uma referência a System.Numerics.

Jeremy McCrorie
fonte