I’ve never been particularly good at math. Sometimes, the numbers get challenging to keep straight, especially when moving in orders of magnitude from one value to another. But, when working with large numbers, I typically codify conversion code to help ease the mental burden of keeping everything straight. I know I need to act, especially once scientific notation shows in my output results.

So, in this post, I’ve written a helper class that helps convert from bytes to kilobytes, megabytes, gigabytes, and terabytes.

How To Convert Bytes To Something Else

For folks just looking for a mathematical formula to convert bytes, it’s pretty straightforward. Every permutation above bytes increases by 1024 ^ N, where N is an integer value.

Technically, the value of 1024 is used for kibibytes, mebibytes, gibibytes and tebibytes.

Use the appropriate value of 1000 or 1024 based on your accuracy needs, but either should give you a ballpark idea of sizes you’ll be dealing with at each level of differentiation.

private enum Kinds
{
    Bytes = 0,
    Kilobytes = 1,
    Megabytes = 2,
    Gigabytes = 3,
    Terabytes = 4,
}

Here is an enumeration showing the increase in N. Of course, you could continue to add more if you need petabytes or exabyte, but that starts to get into excessive digit representations.

To change a higher-order classification (i.e., megabytes) to bytes, you need to multiply value * 1024^N where N in this case is 2. To reverse the process, you need to divide. In our case, going from bytes to megabytes would be value / 1024 ^ 2. It looks like this in code.

public static double To(double value, Kinds kind) => 
    value / Math.Pow(1024, (int)kind);

public static double From(double value, Kinds kind) => 
    value * Math.Pow(1024, (int)kind);

You could stop here, and you have a perfectly efficient way of doing conversions. Still, you could add helper methods to make things easier to read in your existing codebase.

public static class Bytes
{
    private enum Kinds
    {
        Bytes = 0,
        Kilobytes = 1,
        Megabytes = 2,
        Gigabytes = 3,
        Terabytes = 4,
    }

    public static double FromKilobytes(double kilobytes) => 
        From(kilobytes, Kinds.Kilobytes);

    public static double FromMegabytes(double megabytes) => 
        From(megabytes, Kinds.Megabytes);

    public static double FromGigabytes(double gigabytes) => 
        From(gigabytes, Kinds.Gigabytes);

    public static double FromTerabytes(double terabytes) => 
        From(terabytes, Kinds.Terabytes);

    public static double ToKilobytes(double bytes) => 
        To(bytes, Kinds.Kilobytes);

    public static double ToMegabytes(double bytes) => 
        To(bytes, Kinds.Megabytes);

    public static double ToGigabytes(double bytes) => 
        To(bytes, Kinds.Gigabytes);

    public static double ToTerabytes(double bytes) => 
        To(bytes, Kinds.Terabytes);

    private static double To(double value, Kinds kind) => 
        value / Math.Pow(1024, (int)kind);

    private static double From(double value, Kinds kind) => 
        value * Math.Pow(1024, (int)kind);
}

Here’s a unit test class that helps verify everything is working.

using Xunit;

public class BytesTests
{
   public class From
   {
      [Fact]
      public void Kilobytes()
      {
         var bytes = Bytes.FromKilobytes(1);
         Assert.Equal(1024, bytes);
      }

      [Fact]
      public void Megabytes()
      {
         var bytes = Bytes.FromMegabytes(1);
         Assert.Equal(1_048_576, bytes);
      }

      [Fact]
      public void Gigabytes()
      {
         var gigabyte = 1;
         var bytes = Bytes.FromGigabytes(gigabyte);
         Assert.Equal(Math.Pow(1024, 3) * gigabyte, bytes);
      }

      [Fact]
      public void Terabytes()
      {
         var terabyte = 1;
         var bytes = Bytes.FromTerabytes(terabyte);
         Assert.Equal(Math.Pow(1024, 4) * terabyte, bytes);
      }
   }

   public class To
   {
      [Fact]
      public void Kilobytes()
      {
         var kb = Bytes.ToKilobytes(1);
         Assert.Equal(0.0009765625, kb);
      }

      [Fact]
      public void Megabytes()
      {
         var bytes = Bytes.ToMegabytes(1_000_000);
         Assert.Equal(0.9537,  Math.Round(bytes, 4));
      }

      [Fact]
      public void Gigabytes()
      {
         var bytes = Bytes.ToGigabytes(1_000_000);
         Assert.Equal(0.000931, Math.Round(bytes, 6));
      }

      [Fact]
      public void Terabytes()
      {
         var gb = Bytes.FromGigabytes(1);
         var bytes = Bytes.ToTerabytes(gb);
         
         Assert.Equal( 0.000977, Math.Round(bytes, 6));
      }
   }
}

Use Humanizer Instead

Humanizer is a library designed to help you manipulate strings, enums, dates, times, timespans, numbers, and quantities. Within the library is a type called ByteSize which has nice helper functions to convert between size groups. The nice thing about having a type is the core value never changes, and you can quickly translate between all delineations.

var fileSize = (10).Kilobytes();

fileSize.Bits      => 81920
fileSize.Bytes     => 10240
fileSize.Kilobytes => 10
fileSize.Megabytes => 0.009765625
fileSize.Gigabytes => 9.53674316e-6
fileSize.Terabytes => 9.31322575e-9

In addition (no pun intended), you can do math between multiple sizes.

var total = (10).Gigabytes() + (512).Megabytes() - (2.5).Gigabytes();
total.Subtract((2500).Kilobytes()).Add((25).Megabytes());

It’s definitely worth checking out if you think you’ll be needing both conversions and ways to display your results between all the variations. To get started, install the latest version using the dotnet-cli.

dotnet add package Humanizer

How About Using UnitsNet

If you need more options, I also recently found out about UnitsNet, which looks like a great option for all different units of measure. Here’s a sneak peek of what the library can do with Bytes.

using UnitsNet;

var bytes = Information.FromBytes(1_000_000);
Console.WriteLine($"{bytes.Kilobytes}kb");
Console.WriteLine($"{bytes.Megabytes}mb");
Console.WriteLine($"{bytes.Gigabytes}gb");

The resulting output in the console is what you would expect.

1000kb
1mb
0.001gb

The other advantage to using UnitsNet is it comes with a library of other units and measurements, allowing you to do some very complex calculations while still keeping your code readable. It’s pretty awesome.

// Arithmetic
Length l1 = 2 * Length.FromMeters(1);
Length l2 = Length.FromMeters(1) / 2;
Length l3 = l1 + l2;

// Construct between units
Length distance = Speed.FromKilometersPerHour(80) * TimeSpan.FromMinutes(30);
Acceleration a1 = Speed.FromKilometersPerHour(80) / TimeSpan.FromSeconds(2);
Acceleration a2 = Force.FromNewtons(100) / Mass.FromKilograms(20);
RotationalSpeed r = Angle.FromDegrees(90) / TimeSpan.FromSeconds(2);

I hope this helps folks that may be doing conversions in their code. Thanks for reading, and I wish you the best of luck in your development journey. Cheers!