Ian Vink
Ian Vink

Reputation: 68750

Currency Formatting: Remove insignificant Zeros

I am displaying a currency value of up to 5 decimal places with {0:C5} but when there are insignificant zeros, they should not get shown.

With a simple decimal I simply use {0:0.#####} but I need this with Currency Formatters as it's a multi-language system.

Edit: Forgot to mention I am using composite formatting https://msdn.microsoft.com/en-us/library/txafckwd(v=vs.110).aspx

Upvotes: 2

Views: 2357

Answers (4)

Vignesh.N
Vignesh.N

Reputation: 2666

A solution is to use IFormatProvider and ICustomFormatter

public class NumberFormatter : IFormatProvider, ICustomFormatter
    {
        //TODO: Get your own NumberFormatInfo from CurrentCulture instead of this.
        private static readonly NumberFormatInfo NumberFormatInfo = CultureInfo.CreateSpecificCulture("en-US").NumberFormat;

        public object GetFormat(Type formatType)
        {
            if (formatType != typeof(ICustomFormatter))
            {
                throw new InvalidOperationException("Invalid Format");
            }

            return this;
        }

        public string Format(string format, object arg, IFormatProvider formatProvider)
        {
            var supportedTypes = new Type[] { typeof(decimal), typeof(double), typeof(int), typeof(float) };

            if (!supportedTypes.Contains(arg.GetType()))
            {
                return null;
            }

            if (format.ToUpper() != "C")
            {
                return null;
            }

            var result = arg.ToString();

            var isFraction = result.IndexOf(".", System.StringComparison.Ordinal) > 0;
            if (isFraction)
            {

                result = RemoveTrailingZero(result);
                var numberOfDigits = result.Substring(result.IndexOf(".", System.StringComparison.Ordinal) + 1).Length;

                if (numberOfDigits < 5)
                {
                    NumberFormatInfo.CurrencyDecimalDigits = numberOfDigits;
                    NumberFormatInfo.NumberDecimalDigits = numberOfDigits;
                }
                else
                {
                    NumberFormatInfo.CurrencyDecimalDigits = 5;
                    NumberFormatInfo.NumberDecimalDigits = 5;
                }
            }
            return Convert.ToDecimal(result).ToString("C", NumberFormatInfo);
        }

        private string RemoveTrailingZero(string number)
        {
            var lastIndexOfZero = number.LastIndexOf("0", System.StringComparison.Ordinal);
            if (lastIndexOfZero == number.Length - 1)
            {
                number = number.Remove(lastIndexOfZero);
                return RemoveTrailingZero(number);
            }
            else
            {
                return number;
            }
        }
    }

Below is the usage and output

        var numberFormatter = new NumberFormatter();
        var amount = 10000.1230M;
        Console.WriteLine(string.Format(numberFormatter, "Amount is {0:C}", amount));
        amount = 10000.12305M;
        Console.WriteLine(string.Format(numberFormatter, "Amount is {0:C}", amount));
        amount = 10000.00M;
        Console.WriteLine(string.Format(numberFormatter, "Amount is {0:C}", amount));
        amount = 10000.1010M;
        Console.WriteLine(string.Format(numberFormatter, "Amount is {0:C}", amount));

Amount is $10,000.123
Amount is $10,000.12305
Amount is $10,000
Amount is $10,000.101

Upvotes: 1

Fabio
Fabio

Reputation: 32445

You can retrive amount of significant digits from decimal value and then generate currency format with number you get

private string GenerateCurrencyFormatFor(decimal value)
{
    const decimal NORMALIZE_COEFFICENT = 1.000000000000000000000000000000000m;
    const int MAX_DIGITS = 5;

    decimal normalizeValue = value/ NORMALIZE_COEFFICENT;

    var decimalBits = decimal.GetBits(normalizeValue);
    var decimalScaleInfo = decimalBits[3];

    var scaleInfoBytes = BitConverter.GetBytes(decimalScaleInfo);
    var significantDigitsCount = (int)scaleInfoBytes[2];

    var actualDigitsCount = Math.Min(significantDigitsCount, MAX_DIGITS);

    return $"C{actualDigitsCount}";
}

Of course you can "refactor" that function even in one liner, but I want it so to show the idea.

Then use it

var value = 123.4500m;  
var currencyFormat = GenerateCurrencyFormatFor(value);
var printValue = value.ToString(currencyFormat);
// will print £123.45 

var value = 123.1234567m; // will print £123,12346

Upvotes: 2

abatishchev
abatishchev

Reputation: 100268

Maybe put currency symbol explicitly? e.g.:

String.Format(cultureInfo, "{0}{1:0.#####}", cultureInfo.NumberFormat.CurrencySymbol, 1.00005M))

Also see How to Remove the Trailing Zeros of Precision From a C# Decimal

Upvotes: 1

Yaser
Yaser

Reputation: 5719

You can use .TrimEnd(new Char[] { '0' } ) after your normal formatting to get rid of leading zeros.

var number = (0.476434323).ToString("C5").TrimEnd(new Char[] { '0' } );

Upvotes: 1

Related Questions