Użycie formatu ciągu do wyświetlenia po przecinku do 2 miejsc lub prostej liczby całkowitej

291

Mam pole ceny do wyświetlenia, które czasami może wynosić 100, 100,99 lub 100,9. Chcę wyświetlać cenę z dokładnością do 2 miejsc po przecinku tylko wtedy, gdy liczby dziesiętne są wprowadzane dla tej ceny, na przykład jeśli jest to 100, więc powinna tylko pokaż 100, a nie 100,00, a jeśli cena wynosi 100,2, powinna wyświetlić 100,20 podobnie dla 100,22 powinna być taka sama. Przeszukałem Google i natknąłem się na kilka przykładów, ale nie pasowały one dokładnie do tego, czego chciałem:

// 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"
Pan A.
źródło
1
RE: „Chcę wyświetlać cenę z dokładnością do 2 miejsc po przecinku tylko wtedy, gdy liczby dziesiętne są wprowadzane dla tej ceny” - więc jeśli użytkownik wpisze „100,00”, chcesz wyświetlić „100,00”, ale jeśli wpisze „100” chcesz pokazać tylko „100”? - typy liczb śledzą tylko wartość liczby - nie które z nieistotnych cyfr zostały wprowadzone przez użytkownika, a które nie - w tym celu należy użyć ciągu znaków.
BrainSlugs83
2
@BinaryWorrier Myślę, że to pytanie może być duplikatem, ale ma znacznie lepsze i pełniejsze odpowiedzi. IMO drugi należy oznaczyć jako jego duplikat.
Ryan Gates
wystarczy dodać .Replace („. 00”, „”)
Dave Sumter

Odpowiedzi:

155

Nieelegancki sposób to:

var my = DoFormat(123.0);

Z DoFormatbycia coś takiego:

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

Nie elegancki, ale działa w podobnych sytuacjach w niektórych projektach.

Uwe Keim
źródło
6
To nie jest tak naprawdę pytanie, które zostało zadane - ale gdyby tak było - dlaczego po prostu nie użyć string.Format („{0: 0.00}”). Zastąpić („. 00”, „”)?
BrainSlugs83
18
@ BrainSlugs83: w zależności od bieżącego wątku CurrentCultureseparatorem dziesiętnym może nie być .. Jeśli nie CultureInfo.InvariantCulturejest używany string.Format, musisz sprawdzić wartość CultureInfo.NumberFormat.NumberDecimalSeparator, a to byłaby prawdziwa PITA. :)
Groo
@ Uwe Keim Co jeśli mam 60000int i chcę, żeby tak było 60.000?
Prashant Pimpale
Ta odpowiedź to przypadek „wynalezienia kwadratowego koła”. Nie bierze pod uwagę kultury ani faktu, że była już obsługiwana przez .NET.
bytedev
523

Przepraszamy za reaktywację tego pytania, ale nie znalazłem tutaj właściwej odpowiedzi.

Przy formatowaniu liczb możesz użyć 0jako miejsca obowiązkowego i #opcjonalnego.

Więc:

// 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"

Możesz także łączyć 0z #.

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"

W tym przypadku zawsze używana jest metoda formatowania CurrentCulture. Niektóre kultury .zostaną zmienione na ,.

Odpowiedź na oryginalne pytanie:

Najprostsze rozwiązanie pochodzi z @Andrew ( tutaj ). Więc osobiście użyłbym czegoś takiego:

var number = 123.46;
String.Format(number % 1 == 0 ? "{0:0}" : "{0:0.00}", number)
Gh61
źródło
20
Na początku myślałem, że to powinna być odpowiedź, dopóki kilkakrotnie nie przeczytałem oryginalnego pytania. OP nie jest do końca jasne, czego dokładnie chce, ale wydaje się, że zawsze chce 2 miejsc po przecinku, jeśli ktoś wpisze ułamek. Więc jeśli ktoś wejdzie w 1.1, to będzie chciał 1.10; ten kod by tego nie zrobił.
Doug S
40
Ups, przeczytałem to jeszcze raz i masz rację. To nie jest odpowiednia odpowiedź, ale przynajmniej ktoś może uznać to za przydatne.
Gh61
Co potrzebne OP można osiągnąć dzięki temu: stackoverflow.com/a/33180829/2321042
Andrew
Po prostu uznałem, że jest to użyteczne i (w pewnym stopniu) dopasowanie tego, co BoundField w GridView robi z instrukcją SqlDouble i bez formatu. Musisz podać maksymalną liczbę wyświetlanych znaków. (Przeciwko BoundField, chętnie pokazuję tyle, ile chcesz)
fortboise
Tak, to było przydatne, ale jak pokazać tylko dwa miejsca po przecinku, jeśli występuje po przecinku? tzn. jeśli jest to liczba całkowita, to nie wyświetlaj miejsc po przecinku?
Nigel Fds,
64

Jest to częsty przypadek użycia liczb zmiennoprzecinkowych.

Niestety, wszystkie wbudowane łańcuchy formatu liter (np. F, G, N) nie osiągną tego bezpośrednio.
Na przykład num.ToString("F2")zawsze pokaże 2 miejsca po przecinku, takie jak 123.40.

Będziesz musiał użyć 0.##wzoru, nawet jeśli wygląda na trochę gadatliwego.

Kompletny przykład kodu:

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
źródło
7
Ale chce 123,40, a nie 123,4.
Andrew
8
Nie rozwiązując tego pytania, ale rozwiązując moje. Głosuję za tym, aby wszyscy inni mogli to zobaczyć.
Emad,
46

Stare pytanie, ale moim zdaniem chciałem dodać najprostszą opcję.

Bez tysięcy separatorów:

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

Z tysiącami separatorów:

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

To samo, ale z 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

Jeśli potrzebujesz go w wielu miejscach , użyłbym tej logiki w metodzie rozszerzenia :

public static string ToCoolString(this decimal value)
{
    return value.ToString(value % 1 == 0 ? "N0" : "N2"); // Or F0/F2 ;)
}
Andrzej
źródło
28

próbować

double myPrice = 123.0;

String.Format(((Math.Round(myPrice) == myPrice) ? "{0:0}" : "{0:0.00}"), myPrice);
Yahia
źródło
5
string.Format ((liczba% 1) == 0? "{0: 0}": "{0: 0,00}", liczba);
Patrick
8

Zresztą nie wiem, jak ustawić warunek w specyfikatorze formatu, ale możesz napisać własny formatyzator:

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
źródło
6

Oto alternatywa dla metody Uwe Keima, która nadal utrzymywałaby to samo wywołanie metody:

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

Z MyCustomFormatbycia coś takiego:

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
źródło
Nie działało to dla mnie, ponieważ wydaje się, że TrimEnd pobiera tablicę znaków takich jak {',', '.', ''} Zamiast łańcucha takiego jak ".00" - patrz msdn.microsoft.com/en-us/ biblioteka / system.string.trimend.aspx
użytkownik1069816
Masz rację - nie jestem pewien, jak mi tego brakowało. Zaktualizowałem, aby działał poprawnie.
Steve
5
W zależności od bieżącego wątku CurrentCultureseparatorem dziesiętnym może nie być .. O ile nie CultureInfo.InvariantCulturejest używane z string.Format, musisz sprawdzić wartość CultureInfo.NumberFormat.NumberDecimalSeparator, która jest raczej nieelegancka.
Groo
6

Prosty kod jednoliniowy:

public static string DoFormat(double myNumber)
{
    return string.Format("{0:0.00}", myNumber).Replace(".00","");
}
Philip Stuyck
źródło
Problem polega na tym, że jest uruchamiany tam, gdzie separatorem dziesiętnym jest przecinek. Sprawdź komentarze do tej odpowiedzi .
Andrew
6

Jeśli twój program musi działać szybko, wywołaj value.ToString (formatString), aby uzyskać ~ 35% szybszą wydajność formatowania łańcucha w stosunku do $ „{wartość: formatString}” i string.Format (formatString, wartość).

Dane

Wydajność formatowania ciągów C # - VS2017 15.4.5

Kod

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

Kod wyjściowy

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

Bibliografia

Niestandardowe ciągi formatu liczbowego [docs.microsoft.com]

Przykład wykresów Qt BarChart [doc.qt.io]

Neil Justice
źródło
5

Obawiam się, że nie ma wbudowanego formatu, który to zrobiłby. Będziesz musiał użyć innego formatu w zależności od tego, czy wartość jest liczbą całkowitą, czy nie. Lub zawsze formatuj do 2 miejsc po przecinku, a następnie manipuluj ciągiem, aby usunąć końcowe „.00”.

Nikki Locke
źródło
4

Jeśli żadna z pozostałych odpowiedzi nie działa dla Ciebie, może to wynikać z tego, że wiążesz ContentPropertykontrolkę w OnLoadfunkcji, co oznacza, że ​​to nie zadziała:

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

Rozwiązanie jest proste: ContentStringFormatw xaml jest właściwość. Więc kiedy tworzysz etykietę, zrób to:

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

Lub

//if you want the decimal places to be optional
<Label Content="0" Name="labelName" ContentStringFormat="0.##"/>
Brazizzle
źródło
3

coś takiego też będzie działać:

String.Format("{0:P}", decimal.Parse(Resellers.Fee)).Replace(".00", "")
ekkis
źródło
To daje procent?
3

Próbować:

String.Format("{0:0.00}", Convert.ToDecimal(totalPrice));
użytkownik1067656
źródło
2

Aby kod był bardziej przejrzysty, który napisał Kahia (jest przejrzysty, ale staje się trudny, gdy chcesz dodać do niego więcej tekstu) ... wypróbuj to proste rozwiązanie.

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

Musiałem dodać dodatkowy rzut (dziesiętny), aby Math.Round porównał dwie zmienne dziesiętne.

coding_is_fun
źródło
1

To zadziałało dla mnie!

String amount= "123.0000";
String.Format("{0:0.##}", amount);      // "123.00"
Edwin Ikechukwu Okonkwo
źródło
1
To nie działa. Chce, aby 123,00 było wyświetlane jako „123”, a 123,50 jako „123,50”.
Andrew
1

W przypadku liczb dziesiętnych pochodzących z bazy danych (T-) SQL chcesz mieć możliwość konwertowania dziesiętnych i zerowalnych miejsc dziesiętnych z x miejscami dziesiętnymi, a także mieć możliwość łatwego przeglądania kodu w porównaniu z definicjami tabel - i oczywiście wyświetlanie odpowiednia liczba miejsc po przecinku dla użytkownika.

Niestety, Entity Framework nie konwertuje automatycznie czegoś takiego jak SQL decimal(18,2)na ekwiwalent .NET z taką samą liczbą miejsc dziesiętnych (ponieważ dostępny jest tylko ułamek dziesiętny z pełną dokładnością). Musisz ręcznie obciąć miejsca dziesiętne.

Zrobiłem to w ten sposób:

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

Przykładowe użycie:

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
źródło