Remover zeros à direita

177

Tenho alguns campos retornados por uma coleção como

2.4200
2.0044
2.0000

Eu quero resultados como

2.42
2.0044
2

Eu tentei com String.Format, mas ele retorna 2.0000e defini-lo para N0arredondar os outros valores também.

Zo Has
fonte
3
inicialmente o tipo de registro é string ??
Singleton
2
Veja minha resposta: String.Format()com 'G', você deve obter o que deseja. Atualizei minha resposta com um link para os formatos numéricos padrão. Muito usuário.
Dog Ears
3
Um decimal pode ser convertido em double, e o padrão ToStringpara double emite os zeros à direita. leia isto
Shimmy Weitzhandler
2
E provavelmente custará menos desempenho (intervalos de uma quantidade muito grande de registros) do que passar o "G" como um formato de string para a ToStringfunção.
Shimmy Weitzhandler
4
Você não deve converter um decimal em um dobro, pois perderá o significado e poderá introduzir poder de duas imprecisões de arredondamento.
19415 kristianp

Respostas:

167

Não é tão simples assim, se a entrada é uma string? Você pode usar um destes:

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

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

2.0m.ToString("G29")

Isso deve funcionar para todas as entradas.

Atualização Confira os formatos numéricos padrão que tive que definir explicitamente o especificador de precisão para 29, conforme os documentos afirmam claramente:

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 à direita serão preservados

A atualização Konrad apontou nos comentários:

Cuidado com valores como 0,000001. O formato G29 os apresentará da maneira mais curta possível, para mudar para a notação exponencial. string.Format("{0:G29}", decimal.Parse("0.00000001",System.Globalization.CultureInfo.GetCultureInfo("en-US")))dará "1E-08" como resultado.

Orelhas de cão
fonte
1
De todas as respostas, você teve o mínimo de etapas envolvidas. Obrigado orelhas de cão.
Zo tem
4
var d = 2,0 m; d.ToString ("G");
Dave Hillier 31/01
3
@ Dave Hillier - Entendo, corrigi minha resposta. Felicidades. [adicionado "especificador de precisão" explícito]
Dog Orars
16
Cuidado com valores como 0,000001. O formato G29 os apresentará da maneira mais curta possível, para mudar para a notação exponencial. string.Format ("{0: G29}", decimal.Parse ("0.00000001", System.Globalization.CultureInfo.GetCultureInfo ("en-US"))) fornecerá "1E-08" como resultado
Konrad em
15
@ Konrad - existe uma maneira de evitar a notação científica de números com 5 ou 6 casas decimais?
Jill
202

Corri para o mesmo problema, mas em um caso em que não tenho controle da saída para string, que foi resolvida por uma biblioteca. Depois de analisar os detalhes na implementação do tipo Decimal (consulte http://msdn.microsoft.com/en-us/library/system.decimal.getbits.aspx ), criei um truque interessante (aqui como uma extensão método):

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

A parte do expoente do decimal é reduzida para apenas o necessário. Chamar ToString () no decimal de saída escreverá o número sem nenhum 0. à direita.

1.200m.Normalize().ToString();
Thomas Materna
fonte
29
Essa resposta é sua, porque, diferentemente de todas as outras respostas sobre essa questão (e até mesmo todo o assunto), na verdade, FUNCIONA e faz o que o OP está pedindo.
Coxy
2
o número de 0s é uma quantidade exata aqui ou apenas para cobrir os valores mais esperados?
Simon_Weaver 23/09
3
O MSDN declara "O fator de escala é implicitamente o número 10, aumentado para um expoente que varia de 0 a 28", que eu entendo como o número decimal terá no máximo 28 dígitos além do ponto decimal. Qualquer número de zeros> = 28 deve funcionar.
Thomas Materna
2
Esta é a melhor resposta! O número na resposta tem 34 algarismos, 1seguido de 33- 0s, mas isso cria exatamente a mesma decimalinstância de um número com 29 algarismos, um 1e 28- 0s. Assim como @ThomasMaterna disse em seu comentário. Não System.Decimalpode ter mais de 29 algarismos (números com dígitos iniciais maiores que 79228...apenas 28 algarismos no total). Se você quiser mais zeros à direita, multiplique em 1.000...mvez de dividir. Além disso, Math.Roundpode cortar alguns zeros, por exemplo , Math.Round(27.40000m, 2)27.40m, restando apenas um zero.
Jeppe Stig Nielsen
2
Grande truque, mas não funciona em Mono e não é guarantueed ao trabalho em versões de recurso do .NET
Bigjim
87

Na minha opinião, é mais seguro usar seqüências de formato numérico personalizadas .

decimal d = 0.00000000000010000000000m;
string custom = d.ToString("0.#########################");
// gives: 0,0000000000001
string general = d.ToString("G29");
// gives: 1E-13
Michał Powaga
fonte
25
+1 para a única resposta que se baseia em comportamento estável e documentado.
sinelaw
3
Você deve colocar caracteres de 28 '#', consulte blackwasp.co.uk/DecimalTrailingZeroes.aspx .
Jaime Hablutzel
32

Eu uso esse código para evitar a notação científica "G29":

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

EDIT: usando o sistema 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
fonte
1
Esta é uma resposta maravilhosa. É muito simples e você pode ver exatamente o que está acontecendo, em vez de usar um formatador de cordas mágicas.
Timothy Gonzalez
9
Bom, mas não funciona para culturas que usam vírgula para o separador decimal. Você precisaria usarCulture.NumberFormat.NumberDecimalSeparator
blearyeye 12/01
1
Melhor abordagem. Tudo o resto é desnecessariamente complicado. Lembre-se sempre de verificar se o seu número contém ponto decimal.
RRM
1
Falta ser uma extensão "this" antes do parâmetro: public static string DecimalToString (dec decimal)
João Antunes
Muito obrigado pelos comentários. Atualizei a resposta com uma nova versão com as dicas.
x7BiT 29/02
19

Use o #símbolo de hash ( ) para exibir apenas os zeros à direita quando necessário. Veja os testes abaixo.

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
fonte
7
Não é uma resposta. Você não está removendo zeros à direita, está removendo a precisão. O método proposto num1.ToString("0.##")deve retornar 13.1534545765(sem alteração) porque não há zeros à direita.
Jan 'splite' K.
E é exatamente por isso que estou exibindo as respostas para minhas três entradas propostas, para que os usuários possam ver como minha solução funciona.
Fizzix
Ele não responde à pergunta mesmo se você listar as entradas propostas.
Dave Friedel
2
Eu gostaria de votar isso 10 vezes, se eu pudesse. Exatamente o que eu precisava!
precisa saber é o seguinte
1
Resposta perfeita para a pergunta. Melhor resposta na minha opinião. Comentar que remove a precisão está correto, mas esse não foi o problema. Mais importante - é o que eu preciso. Meus usuários normalmente inserem números inteiros, talvez algo como 2,5, mas eu preciso suportar três dígitos além do ponto decimal. Então, eu quero dar a eles o que eles entraram, não com um monte de zeros que eles não entraram. por exemplo, Console.Write ($ "num3 = {num3: 0. ##}"); => num3 = 30
bobwki 19/03
2

Uma abordagem de nível muito baixo, mas acredito que essa seria a maneira mais eficiente de usar apenas cálculos inteiros rápidos (e nenhum método de análise de seqüência lenta e métodos sensíveis à cultura):

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
fonte
1
Eu tinha tentado seguir esse caminho, mas o otimizei ainda mais para que ele ficasse muito mais rápido que o seu, por exemplo, não dividindo as oi e média ints (você ae b) em toda a iteração, se todas forem zero. No entanto, encontrei esta solução surpreendentemente simples que também supera facilmente o que você e eu criamos em termos de desempenho .
Eugene Beresovsky
2

Isso funcionará:

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

Ou se o seu valor inicial for string:

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

Preste atenção a este comentário .

Shimmy Weitzhandler
fonte
2
@ Damien, sim, e observe que, para essas puroposes (você não sentirá nada a menos que faça um bilhão de registros), primeiro porque double é mais rápido, segundo, porque passar um formato de string para a função ToString custa mais desempenho do que não passando parâmetros. novamente, você não sentirá nada a menos que trabalhe em bilhões de registros.
Shimmy Weitzhandler
1
Você não precisa especificar G, porque double.ToStringremove zeros à direita por padrão.
Shimmy Weitzhandler
4
Cuidado com valores como 0,000001. Estes serão apresentados na notação exponencial. double.Parse ("0.00000001", System.Globalization.CultureInfo.GetCultureInfo ("en-US")). ToString () fornecerá "1E-08" como resultado
Konrad em
17
NUNCA faça isso. Normalmente, o motivo de você ter um número decimal é porque você está representando um número com precisão (não exatamente como o dobro). É verdade que esse erro de ponto flutuante é provavelmente muito pequeno, mas pode resultar na exibição de números incorretos, no entanto
Adam Tegen 13/13
2
Ai, converter um tipo de dados de 128 bits (decimal) em um tipo de dados de 64 bits (duplo) para formatação, não é uma boa ideia!
avl_sweden
2

Isto é simples.

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

Testado.

Felicidades :)

Rakesh
fonte
1

Depende do que o seu número representa e como você deseja gerenciar os valores: é uma moeda, você precisa de arredondamento ou truncamento, precisa desse arredondamento apenas para exibição?

Se, para exibição, considere a formatação, os números são x.ToString ("")

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

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

Se for apenas um arredondamento, use a sobrecarga Math.Round que requer uma sobrecarga MidPointRounding

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

Se você obtiver seu valor de um banco de dados, considere converter em vez de converter: double value = (decimal) myRecord ["columnName"];

florim
fonte
0

Você pode apenas definir como:

decimal decNumber = 23.45600000m;
Console.WriteLine(decNumber.ToString("0.##"));
Danilo Ferreira
fonte
1
Para que isso funcione, você deve colocar vinte e oito caracteres '#' em vez de apenas dois.
Jaime Hablutzel
sim, eu entendi mal a pergunta dele ... = /
Danilo Ferreira
0

Caso deseje manter o número decimal, tente o seguinte exemplo:

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

Tentando fazer uma solução mais amigável do 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());
}

Resultado:

=>1
=>1.000000023
=>1.000000023
=>
Dmitri
fonte
-1

Resposta muito simples é usar TrimEnd (). Aqui está o resultado,

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

A saída é 1 Se meu valor for 1,01, minha saída será 1,01

Raj De Inno
fonte
4
E se value = 100?
Stephen Drew
4
@Raj De Inno Mesmo que o valor seja 1,00, a saída que está dando é "1" não "1"
Simba
-1

O código a seguir pode ser usado para não usar o tipo de sequência:

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

Retorna 789,5

Raul Minon
fonte
-2

Truncar Zeros à direita é muito fácil, resolva com uma conversão duplex:

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

(I) -> Analisar valor decimal de qualquer fonte de string.

(II) -> Primeiro: lance para dobrar e remova os zeros à direita. Segundo: Outra conversão para decimal porque não existe conversão implícita de decimal para dupla e vice-versa)

amedriveroperez
fonte
5
Você assume todos os valores que cabem em um decimal, cabem em um dobro. Isso pode causar um OverflowExeception. Você também pode perder precisão.
Martin Mulder
-2

tente este código:

string value = "100";
value = value.Contains(".") ? value.TrimStart('0').TrimEnd('0').TrimEnd('.') : value.TrimStart('0');
Raju
fonte
Por que duas respostas, meu amigo?
Raging Bull
2
E as localidades que não usam '.'?
Martin Mulder
não é uma opção em comparação com outras abordagens neste post. Convertendo para string e volta é sempre ruim opção para manipular números (pelo menos por causa do locale)
Vladimir Semashkin
-11

tente assim

string s = "2.4200";

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

e depois converta isso para flutuar

Singleton
fonte
1
você pode usar subString()método para isso, mas de acordo com pergunta postada aqui, eu não vejo nenhuma exigência como essa =)
Singleton
2
E as localidades que não usam '.'
Dave Hillier 31/01
10
Isso falha miseravelmente para números que são múltiplos múltiplos de 10,0.
Ben Voigt
1
O que o @BenVoigt disse está totalmente correto, pois o exame "12300.00"será aparado "123". Outro efeito que parece indesejável é que alguns números, como por exemplo, "0.00"são cortados até a cadeia vazia ""(embora esse número seja um caso especial de ser um "múltiplo de 10,0" integral).
Jeppe Stig Nielsen