65.9K
CodeProject is changing. Read more.
Home

A simple "human scale" number to string converter

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.88/5 (5 votes)

May 25, 2014

CPOL
viewsIcon

17623

I needed to show file sizes in a "human friendly format" recently, so I thought I'd knock up a simple method to do it. So the file sizes such as 10123456789 bytes would display as 10.1Gb and so forth.

Introduction

I needed to show file sizes in a "human friendly format" recently, so I thought I'd knock up a simple method to do it. So the file sizes such as 10123456789 bytes would display as 10.1Gb and so forth. Simple, but a pain, since it breaks your concentration to "knock up" a method. I'm sure there are other versions out there, but...some of them are far too complicated for their own good!

The code

Easy to read, I think: it uses an array of ISO suffixes (in ISO they are prefixes, because they come before the quantity type: "G" is the prefix, "b" is the bytes in "12.3 Gb")

        /// <summary>
        /// Suffix size values (ISO only goes up to "Yotta", but a 64bit int 
        /// can't even get there - the last two are for completeness.)
        /// </summary>
        /// 
        private static string[] suffixes = new string[] { "", "k", "M", "G", "T", "P", "E", "Z", "Y" };
        /// <summary>
        /// Converts a long value to a human scale count
        ///        Number  Human Scale
        ///             1  1
        ///            10  10
        ///           101  101
        ///          1012  1.01k
        ///         10123  10.1k
        ///        101234  101k
        ///       1012345  1.01M
        ///      10123456  10.1M
        ///     101234567  101M
        ///    1012345678  1.01G
        ///             ....
        /// </summary>
        /// <param name="value">Value to scale</param>
        /// <param name="suffix">If supplied, appended to the output string</param>
        /// <param name="spacer">If supplied, it separates the numbers and the ISO size indicator</param>
        /// <param name="positiveIndicator">If supplied, it prefixes the number</param>
        /// <returns></returns>
        public static string HumanScale(long value, string suffix = "", string spacer = "", string positiveIndicator = "")
            {
            string result = Math.Abs(value).ToString();
            int digits = result.Length;
            if (digits >= 4)
                {
                digits--;       // Groups = (Length - 1) / sizeOfGroup; 
                                // digitsBeforeDecimal = ((Length - 1) % 3) + 1
                                // This avoids testing and "move down" of groups count
                                // when we don't want a decimal at all: 123M for example.
                int groups = digits / 3;
                int digitsBeforeDecimal = digits % 3;
                StringBuilder sb = new StringBuilder();
                foreach (char c in result.Substring(0, 3))
                    {
                    if (digitsBeforeDecimal == -1) sb.Append(System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator);
                    digitsBeforeDecimal--;
                    sb.Append(c);
                    }
                result = string.Format("{0}{1}{2}{3}", sb, spacer, suffixes[groups], suffix);
                }
            return string.Format("{0}{1}", value < 0 ? System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NegativeSign : positiveIndicator, result);
            }

Using it is also simple:

            long l = 1;
            for (int i = 0; i <= 24; i++)
                {
                Console.WriteLine("{0,2} {1,24}  {2}", i, l, HumanScale(l));
                l = (l * 10) + (i % 10);
                }

Generates:

 0                        1  1
 1                       10  10
 2                      101  101
 3                     1012  1.01k
 4                    10123  10.1k
 5                   101234  101k
 6                  1012345  1.01M
 7                 10123456  10.1M
 8                101234567  101M
 9               1012345678  1.01G
10              10123456789  10.1G
11             101234567890  101G
12            1012345678901  1.01T
13           10123456789012  10.1T
14          101234567890123  101T
15         1012345678901234  1.01P
16        10123456789012345  10.1P
17       101234567890123456  101P
18      1012345678901234567  1.01E
19     -8323287284697205938  -8.32E
20      9000847521575698709  9.00E
21     -2225245152790770990  -2.22E
22     -3805707454198158283  -3.80E
23     -1163586394562479596  -1.16E
24      6810880128084755659  6.81E

Note that the long value overflows long before Yottabytes are reached!

History

2014-05-25 Original version

2014-05-25 XML comments un-mucked up. Editor removed closing tags, and so forth - so they would have been invalid in Visual Studio when copy / pasted. I do dislike this editor, sometimes...