Использование формата строки для отображения десятичного числа до 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.