C# format as currency with no trailing zero decima

2019-04-16 02:02发布

问题:

How can we format a number to currency without trailing zero decimal numbers? Basically it should behave as the "C" format specifier but without trailing zeroes. Below are the test cases.

value   | en-US  | fr-FR
1       | $1     | 1 €
1.0     | $1     | 1 €
1.1     | $1.1   | 1,1 €
1.10    | $1.1   | 1,1 €
-1      | ($1)   | -1 €
-1.0    | ($1)   | -1 €
-1.1    | ($1.1) | -1,1 €
1000    | $1,000 | 1 000 €
1000.0  | $1,000 | 1 000 €

Is there a way to achieve this behavior by leveraging the "C" format specifier?

side note: I am continuing from this related wpf question but focusing on the formatting part and with more exhaustive test cases.

回答1:

I think I was able to answer my question with the following:

public static class DecimalExtensions
{
    /// <summary>
    ///     Converts a numeric value to its equivalent currency string representation using the specified culture-specific format information.
    /// </summary>
    /// <param name="value">The value to be converted.</param>
    /// <param name="provider">An object that supplies culture-specific formatting information.</param>
    /// <returns>The currency string representation of the value as specified by <paramref name="provider" />.</returns>
    public static string ToCurrency(this decimal value, IFormatProvider provider) =>
        /// Use "1" (or "-1" if value is negative)
        /// as a placeholder for the actual value.
        (value < 0 ? -1 : 1)

        /// Format as a currency using the "C" format specifier.
        .ToString("C0", provider)

        /// Convert the absolute value to its string representation
        /// then replace the placeholder "1".
        /// We used absolute value since the negative sign
        /// is already converted to its string representation
        /// using the "C" format specifier.
        .Replace("1", Math.Abs(value).ToString("#,0.############################", provider));
}

Tests

public class ToCurrencyTests
{
    private string ToCurrency(decimal value, string cultureName) =>
        value.ToCurrency(CultureInfo.GetCultureInfo(cultureName));

    [Theory]
    [MemberData(nameof(enUS))]
    public void ToCurrency_enUS(decimal value, string expected) =>
    Assert.Equal(expected, ToCurrency(value, "en-US"));

    [Theory]
    [MemberData(nameof(frFR))]
    public void ToCurrency_frFR(decimal value, string expected) =>
        Assert.Equal(expected, ToCurrency(value, "fr-FR"));

    public static TheoryData<decimal, string> enUS =>
        new TheoryData<decimal, string>
        {
            { 1m, "$1" },
            { 1.0m, "$1" },
            { 1.1m, "$1.1" },
            { 1.10m, "$1.1" },
            { -1m, "($1)" },
            { -1.0m, "($1)" },
            { -1.1m, "($1.1)" },
            { 1000m, "$1,000" },
            { 1000.0m, "$1,000" },
            { 123456789.123456789m, "$123,456,789.123456789" },
            { .0000000000000000000000000001m, "$0.0000000000000000000000000001" }
        };

    /// <remarks>
    ///     Note that the group separator used here is a non-breaking space ' ' (i.e. &nbsp; or char 160)
    /// </remarks>
    public static TheoryData<decimal, string> frFR =>
        new TheoryData<decimal, string>
        {
            { 1m, "1 €" },
            { 1.0m, "1 €" },
            { 1.1m, "1,1 €" },
            { 1.10m, "1,1 €" },
            { -1m, "-1 €" },
            { -1.0m, "-1 €" },
            { -1.1m, "-1,1 €" },
            { 1000m, "1 000 €" },
            { 1000.0m, "1 000 €" },
            { 123456789.123456789m, "123 456 789,123456789 €" },
            { .0000000000000000000000000001m, "0,0000000000000000000000000001 €" }
        };
}

EDIT: copy non-breaking space here http://www.unicode-symbol.com/u/00A0.html



回答2:

The following format string removes all trailing zeros (or the whole fraction part if it contains only zero digits).

value.ToString("#,0.#####");

To handle negative numbers, combine that with conditional formatting

value.ToString("#,0.#####;(#,0.#####)");