Видаліть кінцеві нулі


177

У мене є деякі поля, повернуті колекцією як

2.4200
2.0044
2.0000

Я хочу таких результатів

2.42
2.0044
2

Я спробував String.Format, але він повертається 2.0000і встановлює його також для N0округлення інших значень.


3
спочатку тип запису є рядковим ??
Сінглтон

2
Дивіться мою відповідь: String.Format()"G" має отримати те, що ви хочете. Я оновив свою відповідь посиланням на стандартні цифрові формати. Дуже корисний.
Собачні вуха

3
Десяткова частина може бути передана в каси double, а за замовчуванням ToStringдля подвійного виводиться проміжні нулі. читайте це
Шиммі Вайцхандлер

2
І це, ймовірно, буде коштувати меншої продуктивності (інтерми дуже великої кількості записів), ніж передача функції "G" у вигляді рядкового формату ToStringфункції.
Шиммі Вайцхандлер

4
Не слід перетворювати десятковий у подвійний, він втратить значення і може ввести силу двох неточностей округлення.
Крістіанп

Відповіді:


167

Це не так просто, як це, якщо вхід IS рядок? Ви можете скористатися одним із таких:

string.Format("{0:G29}", decimal.Parse("2.0044"))

decimal.Parse("2.0044").ToString("G29")

2.0m.ToString("G29")

Це має працювати для всіх даних.

Оновлення Перевірте стандартні цифрові формати, я повинен був чітко встановити специфікатор точності на 29, оскільки документи чітко зазначають:

Однак, якщо число є десятковою, а специфікатор точності опущений, нотація з фіксованою точкою завжди використовується, а останні нулі зберігаються

Оновлення Конрад зазначив у коментарях:

Слідкуйте за такими значеннями, як 0,000001. Формат G29 представить їх в найкоротші терміни, тому він перейде до експоненціальної позначення. string.Format("{0:G29}", decimal.Parse("0.00000001",System.Globalization.CultureInfo.GetCultureInfo("en-US")))дасть "1E-08" як результат.


1
З усіх відповідей ваші найменші кроки були залучені. Дякую, Собака Вуха
Zo має

4
var d = 2,0 м; d.ToString ("G");
Дейв Хіллер

3
@Dave Hillier - бачу, я виправив свою відповідь. Ура. [додано явний 'уточнювач точності']
Dog Ears

16
Слідкуйте за такими значеннями, як 0,000001. Формат G29 представить їх в найкоротші терміни, тому він перейде до експоненціальної позначення. string.Format ("{0: G29}", decimal.Parse ("0,00000001", System.Globalization.CultureInfo.GetCultureInfo ("en-US"))) дасть "1E-08" як результат
Конрад

15
@Konrad - чи є спосіб уникнути Наукового позначення чисел, що містять 5 або 6 знаків після коми?
Джилл

202

Я зіткнувся з тією ж проблемою, але у випадку, коли я не маю контролю над виведенням на рядок, про що подбала бібліотека. Переглянувши деталі щодо реалізації типу Decimal (див. Http://msdn.microsoft.com/en-us/library/system.decimal.getbits.aspx ), я придумав акуратний трюк (тут як розширення метод):

public static decimal Normalize(this decimal value)
{
    return value/1.000000000000000000000000000000000m;
}

Експонентна частина десятки зводиться до того, що потрібно. Виклик ToString () у вихідній десятковій формі запише число без проміжків 0. Напр

1.200m.Normalize().ToString();

29
Ця відповідь належить тому, що на відміну від кожної іншої відповіді на це питання (і навіть на всю тему) насправді ПРАЦЮЄ і робить те, що задає ОП.
Coxy

2
це число 0s точна кількість тут або просто для покриття найбільш очікуваних значень?
Simon_Weaver

3
MSDN заявляє, що "коефіцієнт масштабування неявно є числом 10, піднятим до показника, що становить від 0 до 28". Я розумію, що десяткове число матиме не більше 28 цифр після десяткової коми. Будь-яка кількість нулів> = 28 повинна працювати.
Томас Матерна

2
Це найкраща відповідь! Число у відповіді має 34 цифри, за 1ними 33-і 0, але це створює абсолютно такий самий decimalекземпляр, як число з 29 фігурами, однією 1та 28 0-ми. Так само, як сказав @ThomasMaterna у своєму коментарі. Не System.Decimalможе мати більше 29 фігур (цифри з провідними цифрами більше, ніж 79228...можуть мати лише 28 цифр). Якщо ви хочете більше нульових знаків, помножте, 1.000...mа не ділимо. Також Math.Roundможе відрізати деякі нулі, наприклад Math.Round(27.40000m, 2)дає 27.40m, тому залишився лише один нуль.
Jeppe Stig Nielsen

2
Чудовий трюк, але НЕ працює над Mono і не гарантується працювати над повнофункціональними версіями .NET
Bigjim

87

На мою думку, безпечніше використовувати користувальницькі рядки числового формату .

decimal d = 0.00000000000010000000000m;
string custom = d.ToString("0.#########################");
// gives: 0,0000000000001
string general = d.ToString("G29");
// gives: 1E-13

25
+1 за єдину відповідь, яка покладається на стабільну, задокументовану поведінку.
sinelaw

3
Ви повинні поставити там 28 "#" знаків, див. Blackwasp.co.uk/DecimalTrailingZeroes.aspx .
Хайме Хаблуцель

32

Я використовую цей код, щоб уникнути наукових позначень "G29":

public static string DecimalToString(this decimal dec)
{
    string strdec = dec.ToString(CultureInfo.InvariantCulture);
    return strdec.Contains(".") ? strdec.TrimEnd('0').TrimEnd('.') : strdec;
}

EDIT: за допомогою системи CultureInfo.NumberFormat.NumberDecimalSeparator:

public static string DecimalToString(this decimal dec)
{
    string sep = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator;
    string strdec = dec.ToString(CultureInfo.CurrentCulture);
    return strdec.Contains(sep) ? strdec.TrimEnd('0').TrimEnd(sep.ToCharArray()) : strdec;
}

1
Це чудова відповідь. Це дуже просто, і ви можете точно побачити, що відбувається, замість того, щоб скористатися форматом чарівних рядків.
Тимофій Гонсалес

9
Приємно, але це не працюватиме для культур, які використовують коми для десяткового роздільника. Вам потрібно буде скористатисяCulture.NumberFormat.NumberDecimalSeparator
blearyeye

1
Найкращий підхід. Все інше зайве складне. Просто пам’ятайте завжди, щоб перевірити, чи містить ваш номер десятковий знак.
RRM

1
Щоб бути розширенням, відсутнє "це" перед параметром: public static string DecimalToString (це десятковий знак dec)
João Antunes

Дуже дякую за коментарі. Я оновив відповідь новою версією з порадами.
x7BiT

19

Використовуйте #символ хеш ( ), щоб відображати лише 0, коли це необхідно. Дивіться тести нижче.

decimal num1 = 13.1534545765;
decimal num2 = 49.100145;
decimal num3 = 30.000235;

num1.ToString("0.##");       //13.15%
num2.ToString("0.##");       //49.1%
num3.ToString("0.##");       //30%

7
Не відповідь. Ви не знімаєте задніх нулів, ви видаляєте точність. Ваш запропонований метод num1.ToString("0.##")повинен повертатись 13.1534545765(без змін), оскільки немає жодних задніх нулів.
Jan 'splite' K.

І саме тому я показую відповіді на три запропоновані мною матеріали, щоб користувачі могли бачити, як працює моє рішення.
Fizzix

Він не відповідає на питання, навіть якщо ви перераховуєте запропоновані матеріали.
Дейв Фрідель

2
Я підняв цю пропозицію 10 разів, якщо я міг. Саме те, що мені було потрібно!
SubqueryCrunch

1
Ідеальна відповідь на питання. НАЙКРАЩА відповідь на мою думку. Прокоментуйте, що це знімає точність, це правильно, але це не питання. Найголовніше - це те, що мені потрібно. Мої користувачі, як правило, вводять цілі числа, можливо, щось на зразок 2,5, але мені потрібно підтримувати 3 цифри поза десятковою комою. Тому я хочу дати їм те, що вони ввели, а не з купою нулів, в які вони не входили. наприклад, Console.Write ($ "num3 = {num3: 0. ##}"); => num3 = 30
bobwki

13

Я знайшов елегантне рішення від http://dobrzanski.net/2009/05/14/c-decimaltostring-and-how-to-get-rid-of-trailing-zeros/

В основному

decimal v=2.4200M;

v.ToString("#.######"); // Will return 2.42. The number of # is how many decimal digits you support.

1
Зверніть увагу , що якщо vє 0mрезультат вашого коду є символом нового рядка , а не "0".
Сем

2
Так. Це повинно бути "0. ########".
PRMan

2

Підхід дуже низького рівня, але я вважаю, що це був би найефективніший спосіб, лише використовуючи швидкі цілі обчислення (а не повільний аналіз рядків та чутливі до культури методи):

public static decimal Normalize(this decimal d)
{
    int[] bits = decimal.GetBits(d);

    int sign = bits[3] & (1 << 31);
    int exp = (bits[3] >> 16) & 0x1f;

    uint a = (uint)bits[2]; // Top bits
    uint b = (uint)bits[1]; // Middle bits
    uint c = (uint)bits[0]; // Bottom bits

    while (exp > 0 && ((a % 5) * 6 + (b % 5) * 6 + c) % 10 == 0)
    {
        uint r;
        a = DivideBy10((uint)0, a, out r);
        b = DivideBy10(r, b, out r);
        c = DivideBy10(r, c, out r);
        exp--;
    }

    bits[0] = (int)c;
    bits[1] = (int)b;
    bits[2] = (int)a;
    bits[3] = (exp << 16) | sign;
    return new decimal(bits);
}

private static uint DivideBy10(uint highBits, uint lowBits, out uint remainder)
{
    ulong total = highBits;
    total <<= 32;
    total = total | (ulong)lowBits;

    remainder = (uint)(total % 10L);
    return (uint)(total / 10L);
}

1
Я намагався пройти цей маршрут, але оптимізував його далі, щоб він дістався набагато швидше, ніж ваш, наприклад, не розділяючи привіт та середину вступів (ваших aта b) під час кожного ітерації, якщо все-таки вони нульові. Однак тоді я знайшов це напрочуд просте рішення, яке також легко перемагає те, що ви і я придумав з ефективністю .
Євген Бересовський

2

Це спрацює:

decimal source = 2.4200m;
string output = ((double)source).ToString();

Або якщо ваше початкове значення string:

string source = "2.4200";
string output = double.Parse(source).ToString();

Зверніть увагу на цей коментар .


2
@Damien, так, і зауважте, що для цих цілей (ви нічого не відчуєте, якщо не зробите мільярд записів), по-перше, тому, що вдвічі швидше, по-друге, тому що передача строкового формату функції ToString коштує більше продуктивності, ніж ні пропуск парами. знову ж, ви нічого не відчуєте, якщо не будете працювати над газиліонами записів.
Шиммі Вайцхандлер

1
Не потрібно вказувати G, бо double.ToStringза замовчуванням видаляється проміжні нулі.
Шиммі Вайцхандлер

4
Слідкуйте за такими значеннями, як 0,000001. Вони будуть представлені в експонентній нотації. подвійний ("0,00000001", System.Globalization.CultureInfo.GetCultureInfo ("en-US")). ToString () дасть "1E-08" як результат
Конрад

17
НІКОЛИ цього не робіть. Зазвичай причина у вас є десятковою, тому що ви точно представляєте число (не приблизно, як у подвійному). Зрозуміло, ця помилка з плаваючою комою, ймовірно, досить мала, але може призвести до відображення невірних чисел, не менше
Адам Теген

2
Так, перетворення 128-бітного типу даних (десятковий) в 64-бітний тип даних (подвійний) для форматування - не дуже гарна ідея!
avl_sweden

2

Це просто.

decimal decNumber = Convert.ToDecimal(value);
        return decNumber.ToString("0.####");

Випробуваний.

Ура :)


1

Залежить від того, що представляє ваше число та як ви хочете керувати значеннями: це валюта, чи потрібне вам округлення чи усічення, чи потрібно це округлення лише для відображення?

Якщо для відображення розглянемо форматування чисел, це x.ToString ("")

http://msdn.microsoft.com/en-us/library/dwhawy9k.aspx і

http://msdn.microsoft.com/en-us/library/0c899ak8.aspx

Якщо це просто округлення, використовуйте перевантаження Math.Round, що вимагає перевантаження MidPointRounding

http://msdn.microsoft.com/en-us/library/ms131274.aspx )

Якщо ви отримуєте своє значення з бази даних, розгляньте можливість кастингу замість конверсії: подвійне значення = (десяткове) myRecord ["columnName"];



0

Якщо ви хочете зберегти десяткове число, спробуйте наступний приклад:

number = Math.Floor(number * 100000000) / 100000000;

0

Намагаючись зробити дружніше рішення DecimalToString ( https://stackoverflow.com/a/34486763/3852139 ):

private static decimal Trim(this decimal value)
{
    var s = value.ToString(CultureInfo.InvariantCulture);
    return s.Contains(CultureInfo.InvariantCulture.NumberFormat.NumberDecimalSeparator)
        ? Decimal.Parse(s.TrimEnd('0'), CultureInfo.InvariantCulture)
        : value;
}

private static decimal? Trim(this decimal? value)
{
    return value.HasValue ? (decimal?) value.Value.Trim() : null;
}

private static void Main(string[] args)
{
    Console.WriteLine("=>{0}", 1.0000m.Trim());
    Console.WriteLine("=>{0}", 1.000000023000m.Trim());
    Console.WriteLine("=>{0}", ((decimal?) 1.000000023000m).Trim());
    Console.WriteLine("=>{0}", ((decimal?) null).Trim());
}

Вихід:

=>1
=>1.000000023
=>1.000000023
=>

-1

Дуже проста відповідь - використовувати TrimEnd (). Ось результат,

double value = 1.00;
string output = value.ToString().TrimEnd('0');

Вихід - 1 Якщо моє значення 1,01, то моє значення буде 1,01


4
А як бути, якщо value = 100?
Стівен Дрю

4
@Raj De Inno Навіть якщо значення дорівнює 1,00, вихід, який він дає, є "1." не "1"
Сімба

-1

Наступний код може бути використаний для використання типу рядка:

int decimalResult = 789.500
while (decimalResult>0 && decimalResult % 10 == 0)
{
    decimalResult = decimalResult / 10;
}
return decimalResult;

Повертається 789,5


-2

Обрізати кінцеві нулі дуже просто, вирішити за допомогою дуплексу:

        decimal mydecimal = decimal.Parse("1,45000000"); //(I)
        decimal truncate = (decimal)(double)mydecimal;   //(II)

(I) -> Десяткове значення розбору з будь-якого джерельного рядка.

(II) -> По-перше: киньте це подвоїти, видаліть кінцеві нулі. По-друге: Інше відтворення в десятковий, оскільки не існує неявного перетворення з десяткового в подвійне і навпаки)


5
Ви припускаєте, що всі значення, які б відповідали десятковій, вміщувались у подвійне. Це може спричинити OverflowExeception. Ви також можете втратити точність.
Мартін Малдер

-2

спробуйте цей код:

string value = "100";
value = value.Contains(".") ? value.TrimStart('0').TrimEnd('0').TrimEnd('.') : value.TrimStart('0');

Чому дві відповіді, мій друже?
Бурхливий Бик

2
Що з локалями, які не використовують "."
Мартін Малдер

зовсім не варіант порівняно з іншими підходами в цій посаді. Перетворення на рядок і назад - це завжди поганий варіант маніпулювання числами (принаймні, через локаль)
Володимир Семашкін

-11

спробуйте так

string s = "2.4200";

s = s.TrimStart('0').TrimEnd('0', '.');

а потім перетворити це в плаваючий


1
ви можете використовувати subString()метод для цього, але відповідно до питання, розміщеного тут, я не бачу жодної такої вимоги =)
Singleton

2
Як щодо локалів, які не використовують '.'
Дейв Хіллєр

10
Це не вдається для чисел, які є кратними 10,0.
Бен Войгт

1
Те, що сказав @BenVoigt, є цілком правильним, оскільки екзамен "12300.00"буде виконаний "123". Інший ефект, який здається небажаним, полягає в тому, що деякі числа, як- "0.00"то, підрізані аж до порожнього рядка ""(хоча це число є особливим випадком того, що воно є інтегральним "кратним 10,0").
Jeppe Stig Nielsen
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.