38

I was refactoring some old code and came across the following line of code to convert bytes to GB.

decimal GB = KB / 1024 / 1024 / 1024;

Is there a better way to refactor the following piece of code?

Update

I meant to say bytes to Gigabytes. I gave wrong information.

Michael Kniskern
  • 24,792
  • 68
  • 164
  • 231
  • 15
    `decimal GB = KB / 1024 / 1024;` would be better... – sth Aug 07 '09 at 00:31
  • 11
    Apart from an extra division, there's nothing wrong with the code as written; it is perfectly clear in its intent, not unnecessarily verbose, and there are no perf problems. Why would you want to refactor it? – Pavel Minaev Aug 07 '09 at 00:35
  • 1
    I think it's fine, though I would probably GB = bytes * 1E-9; – kenny Jan 17 '10 at 22:28
  • 1
    Sorry for being that guy, but that's gibibytes not gigabytes. – vallentin Aug 06 '16 at 18:44

12 Answers12

116

I developed this method here, works up to TB.

private static string FormatBytes(long bytes)
{
    string[] Suffix = { "B", "KB", "MB", "GB", "TB" };
    int i;
    double dblSByte = bytes;
    for (i = 0; i < Suffix.Length && bytes >= 1024; i++, bytes /= 1024) 
    {
        dblSByte = bytes / 1024.0;
    }

    return String.Format("{0:0.##} {1}", dblSByte, Suffix[i]);
}
hdoghmen
  • 3,175
  • 4
  • 29
  • 33
JLopez
  • 1,679
  • 1
  • 12
  • 7
19

If exact precision is not important, use double:

double gb = kb / 1048576D

Agree with Pavel here - there's not really any need to refactor this code... in fact, if this is the biggest problem in your codebase, I think you might be sitting on the most well-written software ever.

Rex M
  • 142,167
  • 33
  • 283
  • 313
  • 2
    +1 on the use of double, but I think the original implementation's intent is clearer. – Marc Aug 07 '09 at 00:40
  • 1
    @Marc probably. I think the gb = kb... part is a pretty good clue though :) – Rex M Aug 07 '09 at 00:41
  • you could probably improve the readability of the value 1048576 by declaring it as a constant with an intent revealing name... – mezoid Aug 15 '09 at 00:52
8

The original code is succinct, easy to read, and with reasonable variable names, self-documenting; I wouldn't change it.

If you absolutely must refactor, you could create a set of extension methods on the numeric types:

public static double BytesToKilobytes(this Int32 bytes)
{
    return bytes / 1024d;
}
public static double BytesToMegabytes(this Int32 bytes)
{
    return bytes / 1024d / 1024d;
}
public static double KilobytesToBytes(this double kilobytes)
{
    return kilobytes * 1024d;
}

//You can then do something like:
double filesize = 32.5d;
double bytes = filesize.KilobytesToBytes();

But unless your code does virtually nothing but convert bytes to kilobytes etc, all this will really do is clutter up Intellisense for no real gain.

technophile
  • 3,556
  • 1
  • 20
  • 24
4

I wrote a small utility class that performs conversions between units, hth..

#region StorageDifferential
/// <summary>
/// Converts between Base 2 or Base 10 storage units [TB, GB, MB, KB, Bytes]
/// </summary>
public enum Differential : int
{
    /// <summary>
    /// Convert Bytes to Kilobytes
    /// </summary>
    ByteToKilo,
    /// <summary>
    /// Convert Bytes to Megabytes
    /// </summary>
    ByteToMega,
    /// <summary>
    /// Convert Bytes to Gigabytes
    /// </summary>
    ByteToGiga,
    /// <summary>
    /// Convert Bytes to Teraytes
    /// </summary>
    ByteToTera,
    /// <summary>
    /// Convert Kilobytes to Bytes
    /// </summary>
    KiloToByte,
    /// <summary>
    /// Convert Kilobytes to Megabytes
    /// </summary>
    KiloToMega,
    /// <summary>
    /// Convert Kilobytes to Gigabytes
    /// </summary>
    KiloToGiga,
    /// <summary>
    /// Convert Kilobytes to Terabytes
    /// </summary>
    KiloToTera,
    /// <summary>
    /// Convert Megabytes to Bytes
    /// </summary>
    MegaToByte,
    /// <summary>
    /// Convert Megabytes to Kilobytes
    /// </summary>
    MegaToKilo,
    /// <summary>
    /// Convert Megabytes to Gigabytes
    /// </summary>
    MegaToGiga,
    /// <summary>
    /// Convert Megabytes to Terabytes
    /// </summary>
    MegaToTera,
    /// <summary>
    /// Convert Gigabytes to Bytes
    /// </summary>
    GigaToByte,
    /// <summary>
    /// Convert Gigabytes to Kilobytes
    /// </summary>
    GigaToKilo,
    /// <summary>
    /// Convert Gigabytes to Megabytes
    /// </summary>
    GigaToMega,
    /// <summary>
    /// Convert Gigabytes to Terabytes
    /// </summary>
    GigaToTerra,
    /// <summary>
    /// Convert Terabyte to Bytes
    /// </summary>
    TeraToByte,
    /// <summary>
    /// Convert Terabyte to Kilobytes
    /// </summary>
    TeraToKilo,
    /// <summary>
    /// Convert Terabytes to Megabytes
    /// </summary>
    TeraToMega,
    /// <summary>
    /// Convert Terabytes to Gigabytes
    /// </summary>
    TeraToGiga,
}
#endregion

#region Storage Sizes
/// <summary>
/// Enumeration of recognized storage sizes [in Bytes]
/// </summary>
public enum StorageSizes : long
{
    /// <summary>
    /// Base 10 Conversion
    /// </summary>
    KILOBYTE = 1000,
    MEGABYTE = 1000000,
    GIGABYTE = 1000000000,
    TERABYTE = 1000000000000,
    /// <summary>
    /// Base 2 Conversion
    /// </summary>
    KIBIBYTE = 1024,
    MEBIBYTE = 1048576,
    GIBIBYTE = 1073741824,
    TEBIBYTE = 1099511627776,
}
#endregion

#region StorageBase
/// <summary>
/// Storage powers 10 based or 1024 based
/// </summary>
public enum StorageBase : int
{
    /// <summary>
    /// 1024 Base power, Typically used in memory measurements
    /// </summary>
    BASE2,
    /// <summary>
    /// 10 Base power, Used in storage mediums like harddrives
    /// </summary>
    BASE10,
}
#endregion

/// <summary>
/// Convert between base 1024 storage units [TB, GB, MB, KB, Byte]
/// </summary>
public static class StorageConverter
{
    /// <summary>
    /// Convert between base 1024 storage units [TB, GB, MB, KB, Byte]
    /// </summary>
    /// <param name="SizeDifferential">Storage conversion differential [enum]</param>
    /// <param name="UnitSize">Size as mutiple of unit type units [double]</param>
    /// <param name="BaseUnit">Size of the base power [enum]</param>
    /// <returns>Converted unit size [double]</returns>
    public static double Convert(Differential SizeDifferential, double UnitSize, StorageBase BaseUnit = StorageBase.BASE10)
    {
        if (UnitSize < 0.000000000001) return 0;

        double POWER1 = 1000;
        double POWER2 = 1000000;
        double POWER3 = 1000000000;
        double POWER4 = 1000000000000;

        if (BaseUnit == StorageBase.BASE2)
        {
            POWER1 = 1024;
            POWER2 = 1048576;
            POWER3 = 1073741824;
            POWER4 = 1099511627776;
        }

        switch (SizeDifferential)
        {
            case Differential.ByteToKilo:
                return UnitSize / POWER1;
            case Differential.ByteToMega:
                return UnitSize / POWER2;
            case Differential.ByteToGiga:
                return UnitSize / POWER3;
            case Differential.ByteToTera:
                return UnitSize / POWER4;
            case Differential.KiloToByte:
                return UnitSize * POWER1;
            case Differential.KiloToMega:
                return UnitSize / POWER1;
            case Differential.KiloToGiga:
                return UnitSize / POWER2;
            case Differential.KiloToTera:
                return UnitSize / POWER3;
            case Differential.MegaToByte:
                return UnitSize * POWER2;
            case Differential.MegaToKilo:
                return UnitSize * POWER1;
            case Differential.MegaToGiga:
                return UnitSize / POWER1;
            case Differential.MegaToTera:
                return UnitSize / POWER2;
            case Differential.GigaToByte:
                return UnitSize * POWER3;
            case Differential.GigaToKilo:
                return UnitSize * POWER2;
            case Differential.GigaToMega:
                return UnitSize * POWER1;
            case Differential.GigaToTerra:
                return UnitSize / POWER1;
            case Differential.TeraToByte:
                return UnitSize * POWER4;
            case Differential.TeraToKilo:
                return UnitSize * POWER3;
            case Differential.TeraToMega:
                return UnitSize * POWER2;
            case Differential.TeraToGiga:
                return UnitSize * POWER1;
        }

        return 0;
    }
}
JGU
  • 879
  • 12
  • 14
  • This is the best answer on this question. I have tested several answers there and none of them converted the right size for me. I guess it's because they are not made to support Base 10. – Mecanik Jan 11 '23 at 10:44
3

This is a little improvement of the good JLopez's answer. Here you can choose to have or not the units indication and the kilo unit is written with the lowercase "k" (the uppercase one is for Kelvin)

//note: this is the JLopez answer!!
/// <summary>
/// Return size in human readable form
/// </summary>
/// <param name="bytes">Size in bytes</param>
/// <param name="useUnit ">Includes measure unit (default: false)</param>
/// <returns>Readable value</returns>
public static string FormatBytes(long bytes, bool useUnit = false)
    {
        string[] Suffix = { " B", " kB", " MB", " GB", " TB" };
        double dblSByte = bytes;
        int i;
        for (i = 0; i < Suffix.Length && bytes >= 1024; i++, bytes /= 1024)
        {
            dblSByte = bytes / 1024.0;
        }
        return $"{dblSByte:0.##}{(useUnit ? Suffix[i] : null)}";
    }
tedebus
  • 978
  • 13
  • 20
2

Personally I'd write it like this: decimal GB = KB / (1024 * 1024); but there's really no need to refactor the code as written.

mattnewport
  • 13,728
  • 2
  • 35
  • 39
2

Well, the formula is wrong (there's only about a million kilobytes in a gigabyte, not a thousand million) but, other than that, it's fine. Anyone used to working with these numbers will know what it means.

One thing I would watch out for (and I don't know if this is a problem with C#) is that a compiler may not be able to optimize the x/1024/1024 if x is not a basic type. With C and integers, the compiler would quite easily turn that into a "blindingly-fast-shift-right-by-20-bits" instruction.

If decimal is a class rather than a basic type, the compiler may have to do two divide operations. Whether this has any real impact on speed (or even whether it happens at all) is outside of my sphere of knowledge.

One thing I'd consider changing is the actual variable names. It makes no real difference to the compiled code but I prefer longer variable names rather than abbreviations, so I'd opt for kiloBytes/gigaBytes or something like that. KB/GB is too easy to get confused with constants, depending on your coding standards.

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • decimal is a built-in type, not a class. But it doesn't support bitshifts either, even though they would make sense. Perhaps the compiler can use them in this scenario even though the programmer cannot. – Ben Voigt Aug 20 '14 at 03:15
2

To make sure that the compiler pre-calculates the divisors:

decimal GB = KB / (1024 * 1024);

Note that you are actually calculating GiB (gibibyte), not GB (gigabyte). If you really want to calculate GB, that would be:

decimal GB = KB / (1000 * 1000);
Guffa
  • 687,336
  • 108
  • 737
  • 1,005
  • 1
    In general usage, gigabyte is used to refer to 1024^3 bytes, mega for 1024^2, kilo for 1024. It may not be technically correct, but I don't think anyone (else) here minds. – snarf Aug 07 '09 at 01:30
  • Yes, most people are not aware of the standard eventhough it's been around for more than ten years, that's why I made a note about it... – Guffa Aug 07 '09 at 07:14
  • The "standard" is merely no real one, since it changed the common meanings of the words and created new words for common things. Thats why really no one accepts this standard, appart from the industry that made it. – Oliver Friedrich Aug 08 '09 at 13:42
  • 1
    @BeowulfOF: What you say is not true. I have seen several programs using the standardised units. It's a real standard, defined by IEC and accepted by several large standardisation bodies like IEEE and CIPM. The standard did not change the commmon meaning of the units as there was no common meaning. Take for example the case of "1.44 MB" floppy disks, where the capcaity was neither 1.44 MB nor 1.44 MiB, but 1.44 * 1024 * 1000 bytes... – Guffa Aug 08 '09 at 14:15
2

In theory, this is faster (precomputing the constant to do multiplication instead of division). It's probably not used often enough to matter, but just in case.

double const KbToGbFactor = 1d / 1024 /1024;

double gb = kb * KbToGbFactor;
Kenan E. K.
  • 13,955
  • 3
  • 43
  • 48
  • 1
    I'm pretty sure this will be optimized when the program is compiled. It is more a matter of taste though whether or not you want a const here. – Spoike Jan 17 '10 at 22:29
  • I'm not so sure - http://blogs.msdn.com/ericlippert/archive/2009/06/11/what-does-the-optimize-switch-do.aspx – Kenan E. K. Jan 18 '10 at 10:57
  • I like this answer for "clarity" (in my mind), but not for "optimization". Uhg :-) –  Oct 21 '10 at 06:21
2

I needed it the other way around, convert from 3rd party component literal size in words (e.g. "0 bytes", "1.1 MB") into generic size in bytes. so I used it this way:

        private static long UnformatBytes(string sizeInWords)
    {
        if(string.IsNullOrWhiteSpace(sizeInWords))
            return -1;

        string size = sizeInWords.Split(' ').FirstOrDefault();
        double result;
        if (string.IsNullOrWhiteSpace(size) || !double.TryParse(size, out result))
        {
            Debugger.Break();
            return -1;
        }

        int pow;

        if (sizeInWords.IndexOf("byte", StringComparison.OrdinalIgnoreCase) > -1)
            pow = 0;
        else if (sizeInWords.IndexOf("kb", StringComparison.OrdinalIgnoreCase) > -1)
            pow = 1;
        else if (sizeInWords.IndexOf("mb", StringComparison.OrdinalIgnoreCase) > -1)
            pow = 2;
        else if (sizeInWords.IndexOf("gb", StringComparison.OrdinalIgnoreCase) > -1)
            pow = 3;
        else if (sizeInWords.IndexOf("tb", StringComparison.OrdinalIgnoreCase) > -1)
            pow = 4;
        else
            return -1;

        return System.Convert.ToInt64((result * Math.Pow(1024, pow)));
    }
EladTal
  • 2,167
  • 1
  • 18
  • 10
0
    public static string BytesToString(this long bytes, string format = "#,##0.00") {
        var unitstr = new string[] { "B", "KB", "MB", "GB", "TB" };
        var bytesd = Convert.ToDouble(bytes);
        var unit = 0;
        while (bytesd / 1024D > 1 && unit < unitstr.Length) {
            unit++; bytesd /= 1024D;
        }
        return string.Format("{0:" + format + "}{1}", bytesd, unitstr[unit]);
    }
Leo
  • 1
  • 1
0
#region AutoFileSize
    public string AutoFileSize(long number)
    {
        double tmp = number;
        string suffix = " B ";
        if (tmp > 1024) { tmp = tmp / 1024; suffix = " KB"; }
        if (tmp > 1024) { tmp = tmp / 1024; suffix = " MB"; }
        if (tmp > 1024) { tmp = tmp / 1024; suffix = " GB"; }
        if (tmp > 1024) { tmp = tmp / 1024; suffix = " TB"; }
        return tmp.ToString("n") + suffix;
    }
    #endregion

long number = (long)fu.PostedFile.ContentLength;
TJ-
  • 14,085
  • 12
  • 59
  • 90