Найкращий спосіб відображення десяткових знаків без зворотних нулів


107

Чи є формат дисплея, який видасть десяткові знаки як ці рядкові представлення в 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. #####" працює однаково. І в будь-якому випадку, це обертається далеко від нуля (просто FYI).
TrueWill

14
@TrueWill Насправді це має значення. d == 0,1 м, тоді "0. #" надає "0.1" і "#. #" робить ".1" без провідних 0. Ні неправильно, ні правильно, просто залежить від того, що ви хочете.
AaronLS

4
Мені потрібно було постійно відображати одне десяткове місце, тому я використовував рядок формату "0.0#".
Коди з Hammer

1
Дивіться відповідь Ервіна Майєра нижче.
shlgug

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, хоча
1212

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

Це хороший момент. Я думаю, що коли я писав це, я мав справу з цінностями, які вже були десятковими або чимось таким чином, я хотів охопити всю справу. Натомість ви можете мати тіло цього методу всередині іншого перевантаженого методу, який натомість приймає рядок.
dislexicanaboko

Це гарне рішення, коли ви можете викликати 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, це культураInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator, незважаючи на регіональні налаштування.
користувач2091150

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','.');до десяткової змінної для перетворення атрибутів Decimalу Stringбез задніх нулів, як у прикладі вище.

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

(ви можете також додати обидва як параметри)


Ви можете цього не усвідомлювати, але, маючи справу з paramsаргументом, вам не доведеться чітко будувати масив. Тож замість багатослівного TrimEnd("0".ToCharArray())ви могли просто написати TrimEnd('0')(примітка: пройшов сингл charзамість а char[]).
Дан Дао

@Dan Tao: Спасибі, я це забув. Минув час, коли я використовував C #. Хоча обидві версії працюють, я редагував свою публікацію.
Мервін

@Mervin: Вам потрібно ввести а char, а не 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.