Usando o formato de seqüência de caracteres para mostrar decimal com até 2 casas ou número inteiro simples

291

Eu tenho um campo de preço para exibir que às vezes pode ser 100 ou 100,99 ou 100,9. O que eu quero é exibir o preço em 2 casas decimais somente se as casas decimais forem inseridas para esse preço, por exemplo, se for 100, deve ser apenas mostre 100 e não 100,00 e se o preço for 100,2, ele deve exibir 100,20 da mesma forma para 100,22 deve ser o mesmo. Pesquisei no Google e encontrei alguns exemplos, mas eles não correspondiam exatamente ao que eu queria:

// just two decimal places
String.Format("{0:0.00}", 123.4567);      // "123.46"
String.Format("{0:0.00}", 123.4);         // "123.40"
String.Format("{0:0.00}", 123.0);         // "123.00"
Sr. A
fonte
4
possível duplicata do decimal Format .NET para dois lugares ou um número inteiro
Binary Worrier
1
RE: "O que eu quero é exibir o preço em duas casas decimais somente se os decimais forem inseridos para esse preço" - portanto, se o usuário digitar "100,00", você deseja mostrar "100,00", mas se digitar "100" você quer apenas mostrar "100"? - tipos de número rastreiam apenas o valor do número - não quais dos dígitos insignificantes foram inseridos por um usuário e quais não foram - para isso você precisará usar uma sequência.
BrainSlugs83
2
@BinaryWorrier Acho que essa pergunta pode ser uma duplicata, mas tem respostas muito melhores e mais completas. OMI, o outro deve ser marcado como uma duplicata deste.
Ryan Gates
basta adicionar .Replace (". 00", "")
Dave Sumter

Respostas:

156

Uma maneira deselegante seria:

var my = DoFormat(123.0);

Com DoFormat sendo algo como:

public static string DoFormat( double myNumber )
{
    var s = string.Format("{0:0.00}", myNumber);

    if ( s.EndsWith("00") )
    {
        return ((int)myNumber).ToString();
    }
    else
    {
        return s;
    }
}

Não é elegante, mas trabalha para mim em situações semelhantes em alguns projetos.

Uwe Keim
fonte
6
Esta não é realmente a pergunta que foi feita - mas foi - por que não usar string.Format ("{0: 0.00}"). Replace (". 00", "")?
BrainSlugs83
18
@ BrainSlugs83: dependendo do segmento atual CurrentCulture, o separador decimal pode não ser .. A menos que CultureInfo.InvariantCultureseja usado com string.Format, você teria que verificar o valor de CultureInfo.NumberFormat.NumberDecimalSeparator, e isso seria uma verdadeira PITA. :)
Groo 15/01
@ Kewe Keim E se eu tiver 60000int e quiser que seja 60.000?
Prashant Pimpale
Esta resposta é um caso de "reinventar uma roda quadrada". Não leva em consideração a cultura ou o fato de que isso já foi tratado pelo .NET.
bytedev 13/04
523

Desculpe por reativar esta pergunta, mas não encontrei a resposta certa aqui.

Na formatação de números, você pode usar 0como local obrigatório e #opcional.

Assim:

// just two decimal places
String.Format("{0:0.##}", 123.4567);      // "123.46"
String.Format("{0:0.##}", 123.4);         // "123.4"
String.Format("{0:0.##}", 123.0);         // "123"

Você também pode combinar 0com #.

String.Format("{0:0.0#}", 123.4567)       // "123.46"
String.Format("{0:0.0#}", 123.4)          // "123.4"
String.Format("{0:0.0#}", 123.0)          // "123.0"

Para este método de formatação é sempre usado CurrentCulture. Para algumas culturas ., será alterado para ,.

Resposta à pergunta original:

A solução mais simples vem do @Andrew ( aqui ). Então, eu pessoalmente usaria algo assim:

var number = 123.46;
String.Format(number % 1 == 0 ? "{0:0}" : "{0:0.00}", number)
Gh61
fonte
20
No começo, pensei que essa seria a resposta, até reler a pergunta original várias vezes. O OP não está totalmente claro o que ele deseja exatamente, mas parece que ele sempre quer duas casas decimais se alguém digitar uma fração. Então, se alguém digitasse 1.1, ele iria querer 1,10; esse código não faria isso.
Doug S
40
Opa, eu li novamente e você está certo. Portanto, essa não é a resposta certa, mas pelo menos alguém pode achar isso útil.
Gh61 29/05
O OP necessária pode ser alcançado com esta: stackoverflow.com/a/33180829/2321042
Andrew
Acabei de achar útil e (de certa forma) igualar o que um BoundField em um GridView faz com um SqlDouble e nenhuma instrução de formato. Você deve indicar o número máximo que irá mostrar. (Vs. BoundField, prazer em mostrar quantos você quiser) #
fortboise 30/12/16
Sim, isso foi útil, mas como mostrar apenas duas casas decimais se houver decimal? ou seja, se é um número inteiro, então não mostra casas decimais?
Nigel Fds
64

Este é um caso de uso de número flutuante de formatação comum.

Infelizmente, todas as seqüências de caracteres de formato de uma letra incorporadas (por exemplo, F, G, N) não conseguem isso diretamente.
Por exemplo, num.ToString("F2")sempre mostrará 2 casas decimais como 123.40.

Você terá que usar o 0.##padrão, mesmo que pareça um pouco detalhado.

Um exemplo de código completo:

double a = 123.4567;
double b = 123.40;
double c = 123.00;

string sa = a.ToString("0.##"); // 123.46
string sb = b.ToString("0.##"); // 123.4
string sc = c.ToString("0.##"); // 123
detale
fonte
7
Mas ele quer 123.40, não 123.4.
Andrew
8
Não resolvendo essa questão, mas resolvendo a minha. Voto isto para que todos os outros vejam.
Emad
46

Pergunta antiga, mas eu queria adicionar a opção mais simples na minha opinião.

Sem milhares de separadores:

value.ToString(value % 1 == 0 ? "F0" : "F2")

Com milhares de separadores:

value.ToString(value % 1 == 0 ? "N0" : "N2")

O mesmo, mas com String.Format :

String.Format(value % 1 == 0 ? "{0:F0}" : "{0:F2}", value) // Without thousands separators
String.Format(value % 1 == 0 ? "{0:N0}" : "{0:N2}", value) // With thousands separators

Se você precisar em muitos lugares , eu usaria essa lógica em um método de extensão :

public static string ToCoolString(this decimal value)
{
    return value.ToString(value % 1 == 0 ? "N0" : "N2"); // Or F0/F2 ;)
}
Andrew
fonte
28

experimentar

double myPrice = 123.0;

String.Format(((Math.Round(myPrice) == myPrice) ? "{0:0}" : "{0:0.00}"), myPrice);
Yahia
fonte
5
string.Format ((número% 1) == 0? "{0: 0}": "{0: 0,00}", número);
Patrick
8

Não sei de maneira alguma colocar uma condição no especificador de formato, mas você pode escrever seu próprio formatador:

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
               // all of these don't work
            Console.WriteLine("{0:C}", 10);
            Console.WriteLine("{0:00.0}", 10);
            Console.WriteLine("{0:0}", 10);
            Console.WriteLine("{0:0.00}", 10);
            Console.WriteLine("{0:0}", 10.0);
            Console.WriteLine("{0:0}", 10.1);
            Console.WriteLine("{0:0.00}", 10.1);

          // works
            Console.WriteLine(String.Format(new MyFormatter(),"{0:custom}", 9));
            Console.WriteLine(String.Format(new MyFormatter(),"{0:custom}", 9.1));
            Console.ReadKey();
        }
    }

    class MyFormatter : IFormatProvider, ICustomFormatter
    {
        public string Format(string format, object arg, IFormatProvider formatProvider)
        {
            switch (format.ToUpper())
            {
                case "CUSTOM":
                    if (arg is short || arg is int || arg is long)
                        return arg.ToString();
                    if (arg is Single || arg is Double)
                        return String.Format("{0:0.00}",arg);
                    break;
                // Handle other
                default:
                    try
                    {
                        return HandleOtherFormats(format, arg);
                    }
                    catch (FormatException e)
                    {
                        throw new FormatException(String.Format("The format of '{0}' is invalid.", format), e);
                    }
            }
            return arg.ToString(); // only as a last resort
        }

        private string HandleOtherFormats(string format, object arg)
        {
            if (arg is IFormattable)
                return ((IFormattable)arg).ToString(format, CultureInfo.CurrentCulture);
            if (arg != null)
                return arg.ToString();
            return String.Empty;
        }

        public object GetFormat(Type formatType)
        {
            if (formatType == typeof(ICustomFormatter))
                return this;
            return null;
        }
    }
}
Tim Hoolihan
fonte
6

Aqui está uma alternativa ao método de Uwe Keim, que ainda manteria a mesma chamada de método:

var example1 = MyCustomFormat(123.1);  // Output: 123.10
var example2 = MyCustomFormat(123.95); // Output: 123.95
var example3 = MyCustomFormat(123);    // Output: 123

ComMyCustomFormat sendo algo como:

public static string MyCustomFormat( double myNumber )
{
    var str (string.Format("{0:0.00}", myNumber))
    return (str.EndsWith(".00") ? str.Substring(0, strLastIndexOf(".00")) : str;
}
Steve
fonte
Isso não funcionou para mim, pois parece que o TrimEnd usa uma matriz de caracteres como {',', '.', ''} Em vez de uma string como ".00" - Consulte msdn.microsoft.com/en-us/ biblioteca / system.string.trimend.aspx
user1069816
Você está certo - não sabe como eu perdi isso. Atualizei para funcionar corretamente.
10133 Steve
5
Dependendo do encadeamento atual CurrentCulture, o separador decimal pode não ser .. A menos que CultureInfo.InvariantCultureseja usado com string.Format, você teria que verificar o valor de CultureInfo.NumberFormat.NumberDecimalSeparator, que é bastante deselegante.
Groo
6

Código de uma linha simples:

public static string DoFormat(double myNumber)
{
    return string.Format("{0:0.00}", myNumber).Replace(".00","");
}
Philip Stuyck
fonte
O problema é se ele for executado onde o separador decimal é uma vírgula. Verifique os comentários para esta resposta .
27419 Andrew Andrew
6

Se o seu programa precisar ser executado rapidamente, chame value.ToString (formatString) para obter um desempenho de formatação de string ~ 35% mais rápido em relação a $ "{value: formatString}" e string.Format (formatString, value).

Dados

Desempenho de formatação de string em C # - VS2017 15.4.5

Código

using System;
using System.Diagnostics;

public static class StringFormattingPerformance
{
   public static void Main()
   {
      Console.WriteLine("C# String Formatting Performance");
      Console.WriteLine("Milliseconds Per 1 Million Iterations - Best Of 5");
      long stringInterpolationBestOf5 = Measure1MillionIterationsBestOf5(
          (double randomDouble) =>
          {
             return $"{randomDouble:0.##}";
          });
      long stringDotFormatBestOf5 = Measure1MillionIterationsBestOf5(
          (double randomDouble) =>
          {
             return string.Format("{0:0.##}", randomDouble);
          });
      long valueDotToStringBestOf5 = Measure1MillionIterationsBestOf5(
          (double randomDouble) =>
          {
             return randomDouble.ToString("0.##");
          });
      Console.WriteLine(
$@"            $""{{value:formatString}}"": {stringInterpolationBestOf5} ms
 string.Format(formatString, value): {stringDotFormatBestOf5} ms
       value.ToString(formatString): {valueDotToStringBestOf5} ms");
   }

   private static long Measure1MillionIterationsBestOf5(
       Func<double, string> formatDoubleUpToTwoDecimalPlaces)
   {
      long elapsedMillisecondsBestOf5 = long.MaxValue;
      for (int perfRunIndex = 0; perfRunIndex < 5; ++perfRunIndex)
      {
         var random = new Random();
         var stopwatch = Stopwatch.StartNew();
         for (int i = 0; i < 1000000; ++i)
         {
            double randomDouble = random.NextDouble();
            formatDoubleUpToTwoDecimalPlaces(randomDouble);
         }
         stopwatch.Stop();
         elapsedMillisecondsBestOf5 = Math.Min(
            elapsedMillisecondsBestOf5, stopwatch.ElapsedMilliseconds);
      }
      return elapsedMillisecondsBestOf5;
   }
}

Saída de código

C# String Formatting Performance
Milliseconds Per 1 Million Iterations - Best Of 5
            $"{value:formatString}": 419 ms
 string.Format(formatString, value): 419 ms
       value.ToString(formatString): 264 ms

Referências

Sequências de formato numérico personalizadas [docs.microsoft.com]

Exemplo de gráfico de barras Qt Charts [doc.qt.io]

Neil Justice
fonte
5

Receio que não exista um formato interno que faça isso. Você precisará usar um formato diferente, dependendo se o valor é um número inteiro ou não. Ou sempre formate com duas casas decimais e manipule a sequência posteriormente para remover qualquer ".00" à direita.

Nikki Locke
fonte
4

Se nenhuma das outras respostas funcionar para você, talvez seja porque você está vinculando o ContentPropertycontrole de uma OnLoadfunção, o que significa que isso não funcionará:

private void UserControl_Load(object sender, RoutedEventArgs e)
{
  Bind.SetBindingElement(labelName, String.Format("{0:0.00}", PropertyName), Label.ContentProperty) 
}

A solução é simples: existe uma ContentStringFormatpropriedade no xaml. Então, quando você criar o rótulo, faça o seguinte:

//if you want the decimal places definite
<Label Content="0" Name="labelName" ContentStringFormat="0.00"/>

Ou

//if you want the decimal places to be optional
<Label Content="0" Name="labelName" ContentStringFormat="0.##"/>
Brazizzle
fonte
3

algo assim também funcionará:

String.Format("{0:P}", decimal.Parse(Resellers.Fee)).Replace(".00", "")
ekkis
fonte
Isso dá um por cento?
3

Experimentar:

String.Format("{0:0.00}", Convert.ToDecimal(totalPrice));
user1067656
fonte
2

Para tornar o código mais claro que o Kahia escreveu (é claro, mas fica complicado quando você deseja adicionar mais texto) ... tente esta solução simples.

if (Math.Round((decimal)user.CurrentPoints) == user.CurrentPoints)
     ViewBag.MyCurrentPoints = String.Format("Your current Points: {0:0}",user.CurrentPoints);
else
     ViewBag.MyCurrentPoints = String.Format("Your current Points: {0:0.0}",user.CurrentPoints);

Eu tive que adicionar o elenco extra (decimal) para ter Math.Round comparar as duas variáveis ​​decimais.

coding_is_fun
fonte
1

Isso funcionou para mim!

String amount= "123.0000";
String.Format("{0:0.##}", amount);      // "123.00"
Edwin Ikechukwu Okonkwo
fonte
1
Isso não funciona. Ele deseja que 123,00 seja mostrado como "123" e 123,50 como "123,50".
Andrew
1

Ao lidar com decimais provenientes de um banco de dados SQL (T-), você deseja converter decimais anuláveis ​​e não anuláveis ​​com x casas decimais e pode revisar facilmente o código em relação às definições de sua tabela - e, é claro, exibir o número certo de casas decimais para o usuário.

Infelizmente, o Entity Framework não converte automaticamente algo como um SQL decimal(18,2) em um equivalente do .NET com o mesmo número de casas decimais (já que existe apenas decimal com precisão total disponível). Você precisa truncar as casas decimais manualmente.

Então, eu fiz assim:

public static class Extensions
{
    public static string ToStringDecimal(this decimal d, byte decimals)
    {
        var fmt = (decimals>0) ? "0." + new string('0', decimals) : "0";
        return d.ToString(fmt);
    }

    public static string ToStringDecimal(this decimal? d, byte decimals)
    {
        if (!d.HasValue) return "";
        return ToStringDecimal(d.Value, decimals);
    }
}

Exemplo de uso:

void Main()
{
    decimal d = (decimal)1.2345;
    decimal? d2 = null; 

    Console.WriteLine(d.ToStringDecinal(2)); // prints: "1.23" (2 decimal places)
    Console.WriteLine(d.ToStringDecinal(0)); // prints: "1" (show integer number)
    Console.WriteLine(d2.ToStringDecimal(2)); // prints: "" (show null as empty string)
}
Matt
fonte