Лучший способ отображения десятичной дроби без конечных нулей


108

Есть ли средство форматирования дисплея, которое будет выводить десятичные числа в качестве этих строковых представлений в C # без округления?

// decimal -> string

20 -> 20
20.00 -> 20
20.5 -> 20.5
20.5000 -> 20.5
20.125 -> 20.125
20.12500 -> 20.125
0.000 -> 0

{0. #} будет округляться, и использование некоторых функций типа Trim не будет работать с привязанным числовым столбцом в сетке.

Ответы:


147

У вас есть максимальное количество десятичных знаков, которое вам когда-либо понадобится? (В ваших примерах не более 5).

Если это так, я думаю, что форматирование с помощью «0. #####» будет делать то, что вы хотите.

    static void Main(string[] args)
    {
        var dList = new decimal[] { 20, 20.00m, 20.5m, 20.5000m, 20.125m, 20.12500m, 0.000m };

        foreach (var d in dList)
            Console.WriteLine(d.ToString("0.#####"));
    }

4
И вы всегда можете накинуть на формат непомерное * количество символов #. * не научный
— Энтони Пеграм

3
+1 - кстати, ведущий # не нужен. «0. #####» работает идентично. И в любом случае, это округление от нуля (просто к сведению).
— TrueWill

14
@TrueWill На самом деле это имеет значение. d == 0.1m, затем "0. #" отображает "0.1", а "#. #" отображает ".1" без начального 0. Ни то, ни другое не является неправильным или правильным, просто зависит от того, что вы хотите.
— AaronLS

4
Мне всегда нужно было отображать один десятичный знак, поэтому я использовал строку формата "0.0#".
— Codes with Hammer

1
См. Ответ Эрвина Майера ниже.
— шлгуг

32

Я только что научился правильно использовать Gспецификатор формата. См. Документацию MSDN . Немного ниже есть примечание, в котором говорится, что конечные нули будут сохраняться для десятичных типов, если точность не указана. Я не знаю, зачем они это сделали, но указание максимального количества цифр для нашей точности должно решить эту проблему. Так что для форматирования десятичных знаков G29это лучший выбор.

decimal test = 20.5000m;
test.ToString("G"); // outputs 20.5000 like the documentation says it should
test.ToString("G29"); // outputs 20.5 which is exactly what we want

21
Это приведет к отображению 0,00001 как 1E-05
— см.

17

Этот строковый формат должен сделать ваш день: «0. ############################». Имейте в виду, что десятичные дроби могут иметь не более 29 значащих цифр.

Примеры:

? (1000000.00000000000050000000000m).ToString("0.#############################")
-> 1000000.0000000000005

? (1000000.00000000000050000000001m).ToString("0.#############################")
-> 1000000.0000000000005

? (1000000.0000000000005000000001m).ToString("0.#############################")
-> 1000000.0000000000005000000001

? (9223372036854775807.0000000001m).ToString("0.#############################")
-> 9223372036854775807

? (9223372036854775807.000000001m).ToString("0.#############################")
-> 9223372036854775807.000000001

4
Это самый общий ответ на проблему. Не уверен, почему это не формат "G" по умолчанию для десятичных чисел. Нули в конце не добавляют информации. В документации Microsoft есть странное предостережение для десятичных чисел : docs.microsoft.com/en-us/dotnet/standard/base-types/ ... Однако, если число является десятичным, а спецификатор точности опущен, всегда используется запись с фиксированной точкой. и конечные нули сохраняются.
— Эрик Роллер

10

Это еще один вариант того, что я видел выше. В моем случае мне нужно сохранить все значащие цифры справа от десятичной точки, что означает удаление всех нулей после самой значащей цифры. Просто подумал, что было бы неплохо поделиться. Я не могу ручаться за эффективность этого, но когда вы пытаетесь достичь эстетики, вы уже в значительной степени прокляты в неэффективности.

public static string ToTrimmedString(this decimal target)
{
    string strValue = target.ToString(); //Get the stock string

    //If there is a decimal point present
    if (strValue.Contains("."))
    {
        //Remove all trailing zeros
        strValue = strValue.TrimEnd('0');

        //If all we are left with is a decimal point
        if (strValue.EndsWith(".")) //then remove it
            strValue = strValue.TrimEnd('.');
    }

    return strValue;
}

Вот и все, просто хотел бросить свои два цента.


3
Это однозначно лучшее решение, оно не взрывается и на «0,0». Я добавил флаг по умолчанию, чтобы целые числа отображались как «1.0», и это только что заставило все мои десятичные дроби выровняться. Не могу понять, почему это не лучший ответ, + лоты.
— Стив Хибберт,

2
Это особенно хорошо, если кто-то уже передал вам строку. Вы можете просто изменить его на ToTrimmedString (эта строка strValue) и удалить первую строку.
— PRMan

Неплохо подмечено. Думаю, когда я писал это, я имел дело со значениями, которые уже были десятичными или что-то в этом роде, поэтому я хотел охватить все это целиком. Вместо этого вы могли бы иметь тело этого метода внутри другого перегруженного метода, который вместо этого принимает строку.
— дислексиканабоко

Это хорошее решение, когда вы можете вызвать ToTrimmedString (), но во многих случаях вы ограничены строкой формата.
— Майк Мариновски

1
Хотя десятичный разделитель должен зависеть от культуры, я думаю, что это хорошее решение. Небольшой вопрос, а почему бы просто не strValue.TrimEnd('0').TrimEnd('.')вместо EndsWithчека?
— nawfal

5

Другое решение, основанное на ответе дислексиканабоко , но не зависящее от текущей культуры:

public static string ToTrimmedString(this decimal num)
{
    string str = num.ToString();
    string decimalSeparator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator;
    if (str.Contains(decimalSeparator))
    {
        str = str.TrimEnd('0');
        if(str.EndsWith(decimalSeparator))
        {
            str = str.RemoveFromEnd(1);
        }
    }
    return str;
}

public static string RemoveFromEnd(this string str, int characterCount)
{
    return str.Remove(str.Length - characterCount, characterCount);
}

Отлично. За исключением SqlDecimal, это CultureInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator, несмотря на региональные настройки.
— user2091150

4

Способ расширения:

public static class Extensions
{
    public static string TrimDouble(this string temp)
    {
        var value = temp.IndexOf('.') == -1 ? temp : temp.TrimEnd('.', '0');
        return value == string.Empty ? "0" : value;
    }
}

Пример кода:

double[] dvalues = {20, 20.00, 20.5, 20.5000, 20.125, 20.125000, 0.000};
foreach (var value in dvalues)
    Console.WriteLine(string.Format("{0} --> {1}", value, value.ToString().TrimDouble()));

Console.WriteLine("==================");

string[] svalues = {"20", "20.00", "20.5", "20.5000", "20.125", "20.125000", "0.000"};
foreach (var value in svalues)
    Console.WriteLine(string.Format("{0} --> {1}", value, value.TrimDouble()));

Вывод:

20 --> 20
20 --> 20
20,5 --> 20,5
20,5 --> 20,5
20,125 --> 20,125
20,125 --> 20,125
0 --> 0
==================
20 --> 20
20.00 --> 2
20.5 --> 20.5
20.5000 --> 20.5
20.125 --> 20.125
20.125000 --> 20.125
0.000 --> 0

1
«20.00 -> 2» Я считаю это плохим поведением. Чтобы исправить это, вызовите TrimEnd два раза: TrimEnd ('0'); TrimEnd (',');
— Вадим

2
НЕ ИСПОЛЬЗУЙТЕ код, написанный выше. @VadymK прав, что его поведение некорректно. Он обрежет конечные нули, которые стоят ПЕРЕД точкой, если точки нет.
— Sevin7,

2
Ответы на stackoverflow не предназначены для копирования и вставки. Они используются как источник обучения . Этот пример кода научит вас использовать IndexOfдля поиска символов в строке. В примере довольно легко настроить решение проблемы без десятичных знаков.
— jgauffin

2

Я не думаю, что это возможно из коробки, но такой простой метод должен сделать это

public static string TrimDecimal(decimal value)
{
    string result = value.ToString(System.Globalization.CultureInfo.InvariantCulture);
    if (result.IndexOf('.') == -1)
        return result;

    return result.TrimEnd('0', '.');
}

2

Сделать "из коробки" довольно просто:

Decimal YourValue; //just as example   
String YourString = YourValue.ToString().TrimEnd('0','.');

это удалит все конечные нули из вашего десятичного числа.

Единственное, что вам нужно сделать, это добавить .ToString().TrimEnd('0','.');к десятичной переменной, чтобы преобразовать a Decimalв Stringбез завершающих нулей, как в примере выше.

В некоторых регионах это должно быть .ToString().TrimEnd('0',',');(где вместо точки используется запятая, но вы также можете добавить точку и запятую в качестве параметров, чтобы быть уверенным).

(вы также можете добавить оба параметра в качестве параметров)


Вы можете этого не осознавать, но при работе с paramsаргументом вам не нужно явно создавать массив. Таким образом, вместо подробного TrimEnd("0".ToCharArray())вы можете просто написать TrimEnd('0')(примечание: передается single charвместо a char[]).
— Дэн Тао

@ Дэн Тао: Спасибо, я забыл об этом. Я давно не использовал C #. Хотя обе версии работают, я отредактировал свой пост.
— Мервин

@Mervin: вам нужно передать a char, а не a string(так: одинарные кавычки, а не двойные кавычки). Я починил это для вас - надеюсь, вы не против.
— Дэн Тао

1
Ваше решение выдаст результат типа «2». когда были только нули.
— jgauffin,

2
Тогда я бы предложил вызвать .ToString (). TrimEnd ('0'). TrimEnd ('.'). Также вместо "." лучше иметь CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator, чтобы он работал в разных культурах.
— Ε Г И І И О


0

Я получил следующий код:

    public static string DropTrailingZeros(string test)
    {
        if (test.Contains(CultureInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator))
        {
            test = test.TrimEnd('0');
        }

        if (test.EndsWith(CultureInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator))
        {
            test = test.Substring(0,
                test.Length - CultureInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator.Length);
        }

        return test;
    }

0

У меня получился такой вариант:

public static string Decimal2StringCompact(decimal value, int maxDigits)
    {
        if (maxDigits < 0) maxDigits = 0;
        else if (maxDigits > 28) maxDigits = 28;
        return Math.Round(value, maxDigits, MidpointRounding.ToEven).ToString("0.############################", CultureInfo.InvariantCulture);
    }

Преимущества:

вы можете указать максимальное количество значащих цифр после точки для отображения во время выполнения;

вы можете явно указать метод раунда;

вы можете явно контролировать культуру.


0

Вы можете создать метод расширения

public static class ExtensionMethod {
  public static decimal simplify_decimal(this decimal value) => decimal.Parse($"{this:0.############}");
}

0

Я сделал следующие методы расширения для себя, чтобы они соответствовали одному из моих проектов, но, возможно, они будут полезны кому-то другому.

using System.Numerics;
using System.Text.RegularExpressions;
internal static class ExtensionMethod
{
    internal static string TrimDecimal(this BigInteger obj) => obj.ToString().TrimDecimal();
    internal static string TrimDecimal(this decimal obj) => new BigInteger(obj).ToString().TrimDecimal();
    internal static string TrimDecimal(this double obj) => new BigInteger(obj).ToString().TrimDecimal();
    internal static string TrimDecimal(this float obj) => new BigInteger(obj).ToString().TrimDecimal();
    internal static string TrimDecimal(this string obj)
    {
        if (string.IsNullOrWhiteSpace(obj) || !Regex.IsMatch(obj, @"^(\d+([.]\d*)?|[.]\d*)$")) return string.Empty;
        Regex regex = new Regex("^[0]*(?<pre>([0-9]+)?)(?<post>([.][0-9]*)?)$");
        MatchEvaluator matchEvaluator = m => string.Concat(m.Groups["pre"].Length > 0 ? m.Groups["pre"].Value : "0", m.Groups["post"].Value.TrimEnd(new[] { '.', '0' }));
        return regex.Replace(obj, matchEvaluator);
    }
}

Хотя для этого потребуется ссылка на System.Numerics.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.