Использование строкового формата для отображения десятичной дроби с точностью до двух знаков или простого целого числа

У меня есть поле цены для отображения, которое иногда может быть либо 100, либо 100,99, либо 100,9. Я хочу, чтобы цена отображалась в двух десятичных разрядах, только если десятичные числа введены для этой цены, например, если ее 100, поэтому он должен только покажите 100, а не 100.00, и если цена будет 100.2, она должна отобразить 100.20, аналогично для 100.22 должно быть то же самое. Я погуглил и наткнулся на несколько примеров, но они не совсем соответствовали тому, что я хотел:

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

person Mr A    schedule 05.08.2011    source источник
comment
возможный дубликат .net Format десятичной дроби до двух знаков или целое число   -  person Binary Worrier    schedule 16.08.2011
comment
RE: Я хочу, чтобы цена отображалась с двумя десятичными знаками, только если для этой цены введены десятичные дроби - поэтому, если пользователь вводит 100,00, вы хотите отобразить 100,00, но если они вводят 100, вы хотите отображать только 100? - числовые типы отслеживают только значение числа - а не то, какие из незначительных цифр были введены пользователем, а какие нет - для этого вам нужно будет использовать строку.   -  person BrainSlugs83    schedule 11.12.2013
comment
@BinaryWorrier Я думаю, что этот вопрос может быть дубликатом, но на него есть гораздо лучшие и полные ответы. ИМО, другой должен быть отмечен как дубликат этого.   -  person Ryan Gates    schedule 31.01.2015
comment
просто добавьте .Replace (.00,)   -  person Dave Sumter    schedule 02.06.2017
comment
Все, что вам нужно, это value.ToString (0. ##);   -  person Mehdi    schedule 17.11.2020
comment
@Mehdi - Ваше предложение не сработает для 100.2. Он хочет получить "100.20", а не "100.2".   -  person ConnorsFan    schedule 15.05.2021
comment
ммм, ты прав! Я должен был прочитать Q более внимательно.   -  person Mehdi    schedule 17.05.2021


Ответы (17)


Неэлегантный способ:

var my = DoFormat(123.0);

Когда DoFormat выглядит примерно так:

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

Не изящно, но у меня работает в подобных ситуациях в некоторых проектах.

person Uwe Keim    schedule 05.08.2011
comment
Это не совсем тот вопрос, который задавали - но если бы он был - почему бы просто не использовать string.Format ({0: 0.00}). Replace (.00,)? - person BrainSlugs83; 11.12.2013
comment
@ BrainSlugs83: в зависимости от CurrentCulture текущего потока десятичный разделитель может отличаться от .. Если CultureInfo.InvariantCulture не используется с string.Format, вам нужно будет проверить значение CultureInfo.NumberFormat.NumberDecimalSeparator, и это будет настоящий PITA. :) - person Groo; 15.01.2014
comment
@Uwe Keim Что делать, если у меня 60000 int, и я хочу, чтобы это было 60.000? - person Prashant Pimpale; 20.03.2019
comment
Этот ответ представляет собой изобретение квадратного колеса. Не принимает во внимание культуру или тот факт, что это уже было обработано .NET. - person bytedev; 13.04.2020

Извините за повторную активацию этого вопроса, но я не нашел здесь правильного ответа.

При форматировании чисел вы можете использовать 0 как обязательное место и # как необязательное место.

Итак:

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

Вы также можете комбинировать 0 с #.

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"

Для этого всегда используется метод форматирования CurrentCulture. Для некоторых культур . будет изменено на ,.

Ответ на исходный вопрос:

Самое простое решение - от @Andrew (здесь). Лично я бы использовал что-то вроде этого:

var number = 123.46;
String.Format(number % 1 == 0 ? "{0:0}" : "{0:0.00}", number)
person Gh61    schedule 13.02.2014
comment
Сначала я подумал, что это должен быть ответ, пока я не перечитал исходный вопрос несколько раз. OP не совсем ясно, что он конкретно хочет, но кажется, что он всегда хочет 2 десятичных знака, если кто-то вводит дробь. Так что, если кто-то вошел в 1.1, он захотел бы 1.10; этот код этого не сделает. - person Doug S; 27.05.2014
comment
Ой, я прочитал это еще раз, и ты прав. Итак, это неправильный ответ, но, по крайней мере, кому-то это может пригодиться. - person Gh61; 29.05.2014
comment
То, что нужно OP, может быть достигнуто с помощью этого: stackoverflow.com/a/33180829/2321042 - person Andrew; 15.07.2016
comment
Я только что нашел это полезным и (в некоторой степени) совпадающим с тем, что BoundField в GridView делает с SqlDouble и без инструкции форматирования. Вы должны указать максимальное количество, которое вы покажете. (Vs. BoundField, с радостью покажу столько или меньше, сколько захотите) - person fortboise; 30.12.2016
comment
Да, это было полезно, но как показать только два десятичных знака, если присутствует десятичный? т.е. если это целое число, то десятичные дроби не отображаются? - person Nigel Fds; 04.10.2017

Это распространенный вариант использования форматирования плавающих чисел.

К сожалению, все встроенные строки однобуквенного формата (например, F, G, N) не могут достичь этого напрямую.
Например, num.ToString("F2") всегда будет отображать 2 десятичных знака, например 123.40.

Вам придется использовать шаблон 0.##, даже если он выглядит немного многословным.

Полный пример кода:

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
person detale    schedule 18.10.2015
comment
Но он хочет 123,40, а не 123,4. - person Andrew; 15.07.2016
comment
Не решая этот вопрос, а решая мой. Я голосую за это, чтобы все увидели. - person Emad; 07.02.2017

Старый вопрос, но я хотел добавить самый простой, на мой взгляд, вариант.

Без разделителей тысяч:

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

С разделителями тысяч:

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

То же, но с 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

Если он вам нужен в многих местах, я бы использовал эту логику в методе расширения:

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

пытаться

double myPrice = 123.0;

String.Format(((Math.Round(myPrice) == myPrice) ? "{0:0}" : "{0:0.00}"), myPrice);
person Yahia    schedule 05.08.2011
comment
string.Format ((число% 1) == 0? {0: 0}: {0: 0.00}, число); - person Patrick; 02.05.2014

Если ваша программа должна работать быстро, вызовите value.ToString (formatString) для повышения производительности форматирования строки примерно на 35% по сравнению с $ "{value: formatString}" и string.Format (formatString, value).

Данные

«Эффективность

Код

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

Вывод кода

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

использованная литература

Строки настраиваемого числового формата [docs.microsoft. ru]

Пример диаграммы BarChart Qt [doc.qt.io]

person Neil Justice    schedule 03.12.2017
comment
Приятно знать, что это можно сделать в нотации $ string. И если он не используется в жестком цикле, то не стоит беспокоиться о снижении производительности. - person Paul Masri-Stone; 20.10.2020
comment
В основном это связано с боксом и другими дополнительными отчислениями. Дополнительные сведения см. В этом ответе. - person l33t; 17.06.2021

Я не знаю, как поставить условие в спецификатор формата, но вы можете написать свой собственный модуль форматирования:

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;
        }
    }
}
person Tim Hoolihan    schedule 05.08.2011

Вот альтернатива методу Уве Кейма, который по-прежнему будет поддерживать тот же вызов метода:

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

Когда MyCustomFormat выглядит примерно так:

public static string MyCustomFormat( double myNumber )
{
    var str (string.Format("{0:0.00}", myNumber))
    return (str.EndsWith(".00") ? str.Substring(0, strLastIndexOf(".00")) : str;
}
person Steve    schedule 09.10.2013
comment
У меня это не сработало, так как кажется, что TrimEnd принимает массив символов вроде {',', '.', ''}, А не строку типа .00 - см. msdn.microsoft.com/en-us/library/system.string.trimend.aspx - person user1069816; 04.12.2013
comment
Вы правы - не знаю, как я это пропустил. Я обновился, чтобы работать правильно. - person Steve; 05.12.2013
comment
В зависимости от CurrentCulture текущего потока десятичный разделитель может отличаться от .. Если CultureInfo.InvariantCulture не используется с string.Format, вам нужно будет проверить значение CultureInfo.NumberFormat.NumberDecimalSeparator, что довольно неэлегантно. - person Groo; 15.01.2014

Простой однострочный код:

public static string DoFormat(double myNumber)
{
    return string.Format("{0:0.00}", myNumber).Replace(".00","");
}
person Philip Stuyck    schedule 17.02.2017
comment
Проблема заключается в том, что он запускается, где десятичным разделителем является запятая. Проверьте комментарии к этому ответу. - person Andrew; 27.02.2019

Боюсь, что нет встроенного формата, который бы это сделал. Вам придется использовать другой формат в зависимости от того, является ли значение целым числом или нет. Или всегда форматируйте до двух знаков после запятой, а затем манипулируйте строкой, чтобы удалить любой завершающий ".00".

person Nikki Locke    schedule 05.08.2011

Пытаться:

String.Format("{0:0.00}", Convert.ToDecimal(totalPrice));
person user1067656    schedule 24.07.2017

Если ни один из других ответов не работает для вас, это может быть связано с тем, что вы привязываете ContentProperty элемента управления в функции OnLoad, что означает, что это не сработает:

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

Решение простое: в файле xaml. Итак, когда вы создаете этикетку, сделайте следующее:

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

Or

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

что-то вроде этого тоже будет работать:

String.Format("{0:P}", decimal.Parse(Resellers.Fee)).Replace(".00", "")
person ekkis    schedule 12.01.2014
comment
Что дает процент? - person ; 26.01.2017

При работе с десятичными знаками, поступающими из базы данных (T-) SQL, вы хотите иметь возможность преобразовывать десятичные дроби, допускающие и не допускающие значение NULL, с x десятичными разрядами и иметь возможность легко проверять код на соответствие определениям ваших таблиц - и, конечно же, отображать нужное количество десятичных знаков для пользователя.

К сожалению, Entity Framework не преобразует автоматически что-то вроде SQL decimal(18,2) в эквивалент .NET с таким же количеством десятичных знаков (поскольку доступна только десятичная дробь с полной точностью). Вы должны усечь десятичные знаки вручную.

Итак, я сделал это так:

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

Пример использования:

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)
}
person Matt    schedule 22.01.2020

Чтобы сделать код более понятным, который написал Кахия (он ясен, но становится сложнее, когда вы хотите добавить в него больше текста) ... попробуйте это простое решение.

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

Мне пришлось добавить дополнительное приведение (десятичное), чтобы Math.Round сравнивал две десятичные переменные.

person coding_is_fun    schedule 06.02.2014

Это сработало для меня!

String amount= "123.0000";
String.Format("{0:0.##}", amount);      // "123.00"
person Edwin O.    schedule 14.03.2015
comment
Это не работает. Он хочет, чтобы 123,00 отображалось как 123, а 123,50 - как 123,50. - person Andrew; 17.10.2015

Аналогичное требование предъявляется к недавнему проекту. Я написал этот метод десятичного расширения, который использует спецификатор формата валюты (C). Помимо удаления нулей, он также имеет параметры для точности десятичных цифр, символа валюты, разделителя и культуры.

public static DecimalExtension{

     public static string ToCurrency(this decimal val, 
                                     int precision = 2, 
                                     bool currencySymbol = false, 
                                     bool separator = false, 
                                     CultureInfo culture = null)
      {     
         if(culture == null) culture = new CultureInfo("en-US");
                                                   
         NumberFormatInfo nfi = culture.NumberFormat;
         nfi.CurrencyDecimalDigits = precision;
            
         string zeros = new String('0', precision);       
                    
         //Remove zeros
         var result = val.ToString("C",fi).Replace(nfi.CurrencyDecimalSeparator + zeros,"");
                     
         if(!separator) result = result.Replace(nfi.CurrencyGroupSeparator,"");
                    
         return currencySymbol? result: result.Replace(nfi.CurrencySymbol,"");      
        }   
}

Примеры:

decimal Total = 123.00M;
Console.WriteLine(Total.ToCurrency());  
//output: 123

decimal Total = 1123.12M;
Console.WriteLine(Total.ToCurrency()); 
//Output:  1123.12

Console.WriteLine(Total.ToCurrency(4));
//Output:  1123.1200

Console.WriteLine(Total.ToCurrency(2,true,true));
//output:  $1,123.12
 
CultureInfo culture = new CultureInfo("pt-BR")  //Brazil
Console.WriteLine(Total.ToCurrency(2,true,true, culture));
//output:  R$ 1.123,12
person Wei    schedule 18.12.2020