Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

A simple human scale number to string converter

0.00/5 (No votes)
25 May 2014 1  
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...

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here