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


292

У меня есть поле для отображения цены, которое иногда может быть либо 100, либо 100,99, либо 100,9. Я хочу, чтобы цена отображалась в 2 десятичных разрядах, только если для этой цены введены десятичные дроби, например, если ее 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"


1
RE: «Я хочу, чтобы цена отображалась в 2 десятичных разрядах, только если для этой цены введены десятичные дроби» - поэтому, если пользователь вводит «100,00», вы хотите отобразить «100,00», но если они вводят «100» ты только хочешь показать "100"? - типы чисел только отслеживают значение числа, а не то, какие из незначительных цифр были введены пользователем, а какие нет, - для этого вам нужно будет использовать строку.
BrainSlugs83

2
@BinaryWorrier Я думаю, что этот вопрос может быть дубликатом, но у него гораздо лучшие и более полные ответы. ИМО другой должен быть помечен как дубликат этого.
Райан Гейтс

1
просто добавьте .Replace (". 00", "")
Дейв Самтер

Ответы:


156

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

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

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


6
Это не тот вопрос, который был задан - но если бы он был - почему бы просто не использовать string.Format ("{0: 0.00}"). Replace (". 00", "")?
BrainSlugs83

18
@ BrainSlugs83: в зависимости от текущего потока CurrentCulture, десятичный разделитель может не быть .. Если CultureInfo.InvariantCultureне используется с string.Format, вам придется проверить значение CultureInfo.NumberFormat.NumberDecimalSeparator, и это будет настоящая PITA. :)
Groo

@ Uwe Keim Что, если у меня есть 60000int, и я хочу, чтобы это было 60.000?
Prashant Pimpale

Этот ответ - случай "изобретать квадратное колесо". Не учитывает культуру или тот факт, что это уже было обработано .NET.
bytedev

524

Извините за возобновление этого вопроса, но я не нашел правильный ответ здесь.

При форматировании чисел вы можете использовать 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)

20
Сначала я думал, что это должен быть ответ, пока я не перечитал исходный вопрос несколько раз. ОП не совсем ясно, что именно он хочет, но, кажется, он всегда хочет 2 знака после запятой, если кто-то вводит дробь. Так что если кто-то введет 1.1, то он захочет 1.10; этот код не сделает этого.
Даг С

40
К сожалению, я прочитал это снова, и вы правы. Так что это не правильный ответ, но по крайней мере кто-то может найти это полезным.
Gh61

Какой ОП необходимого можно достичь с помощью этого: stackoverflow.com/a/33180829/2321042
Эндрю

Я просто нашел это полезным и (в некоторой степени) сопоставляющим то, что BoundField в GridView делает с SqlDouble и без инструкции форматирования. Вы должны указать максимальный #, который вы покажете. (Против BoundField, с удовольствием показываю столько или мало, как вам нравится)
fortboise

Да, это было полезно, но как показать только два десятичных знака, если присутствует десятичный? т.е. если это целое число, то не показывает десятичные дроби?
Найджел Фдс

64

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

К сожалению, все встроенные однобуквенные строки формата (например, 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

7
Но он хочет 123,40, а не 123,4.
Андрей

8
Не решая этот вопрос, но решая мой. Я приветствую это, чтобы подняться на всеобщее обозрение.
Emad

46

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

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

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

28

пытаться

double myPrice = 123.0;

String.Format(((Math.Round(myPrice) == myPrice) ? "{0:0}" : "{0:0.00}"), myPrice);

5
string.Format ((число% 1) == 0? "{0: 0}": "{0: 0.00}", число);
Патрик,

8

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

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

6

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

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

Это не сработало для меня, так как кажется, что TrimEnd принимает массив символов вроде {',', '.', ''}, А не строку типа ".00" - см. Msdn.microsoft.com/en-us/ library / system.string.trimend.aspx
user1069816

Ты прав - не уверен, как я это пропустил. Я обновился, чтобы работать правильно.
Стив

5
В зависимости от текущего потока CurrentCulture, десятичный разделитель может не быть .. Если CultureInfo.InvariantCultureне используется с string.Format, вам придется проверить значение CultureInfo.NumberFormat.NumberDecimalSeparator, которое довольно не элегантно.
Groo

6

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

public static string DoFormat(double myNumber)
{
    return string.Format("{0:0.00}", myNumber).Replace(".00","");
}

Проблема в том, что если он запускается там, где десятичный разделитель - запятая. Проверьте комментарии к этому ответу .
Эндрю

6

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

Данные

Производительность форматирования строки C # - VS2017 15.4.5

Код

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.com]

Пример Qt Barts BarChart [doc.qt.io]


5

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


4

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

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

Решение простое: ContentStringFormatв xaml есть свойство. Поэтому, когда вы создаете ярлык, сделайте это:

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

Или

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

3

как то так тоже будет работать

String.Format("{0:P}", decimal.Parse(Resellers.Fee)).Replace(".00", "")

Что дает процент?


2

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

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 сравнил две десятичные переменные.



1

Имея дело с десятичными числами, поступающими из (T-) базы данных SQL, вы хотите иметь возможность преобразовывать обнуляемые и ненулевые десятичные числа с 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)
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.