See the question and my original answer on StackOverflow

Here are two variants (they need Windows Vista) in C#:

...
Console.WriteLine(FormatByteSize(1031023120)); // 983 MB
Console.WriteLine(FormatByteSize2(1031023120, true)); // 1 006 859 KB
...

Note the benefit (or an inconvenient depending on how you see it) of using Windows is you will get localized version (if any), using the Shell/OS culture.

public static string FormatByteSize2(long size, bool alwaysKb = false)
{
    // Here, we use Windows Shell's size column definition and formatting
    // note although System.Size is defined as a UInt64, formatting doesn't support more than long.MaxValue...
    PSGetPropertyKeyFromName("System.Size", out var pk);
    var pv = new PROPVARIANT(size);
    var sb = new StringBuilder(128);
    const int PDFF_ALWAYSKB = 4;
    PSFormatForDisplay(ref pk, pv, alwaysKb ? PDFF_ALWAYSKB : 0, sb, sb.Capacity);
    return sb.ToString();
}

public static string FormatByteSize(long size)
{
    // Here, we use use a Windows Shell API (probably the sames algorithm underneath)
    // It's much simpler, we only need to declare one StrFormatByteSizeW API
    var sb = new StringBuilder(128);
    StrFormatByteSizeW(size, sb, sb.Capacity);
    return sb.ToString();
}

[DllImport("shlwapi", CharSet = CharSet.Unicode)]
private static extern IntPtr StrFormatByteSizeW(long qdw, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszBuf, int cchBuf);

[DllImport("propsys", CharSet = CharSet.Unicode)]
private static extern int PSFormatForDisplay(
    ref PROPERTYKEY propkey,
    PROPVARIANT pv,
    int pdfFlags,
    [MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszBuf, int cchBuf);

[DllImport("propsys", CharSet = CharSet.Unicode)]
private static extern int PSGetPropertyKeyFromName([MarshalAs(UnmanagedType.LPWStr)] string pszName, out PROPERTYKEY ppropkey);

[StructLayout(LayoutKind.Sequential)]
private struct PROPERTYKEY
{
    public Guid fmtid;
    public int pid;
}

[StructLayout(LayoutKind.Sequential)]
private class PROPVARIANT
{
    // note this version of PROPVARIANT is far from being suited for all purposes...
    public short vt;
    short wReserved1;
    short wReserved2;
    short wReserved3;
    public long val;

    const short VT_UI8 = 21;

    public PROPVARIANT(long ul)
    {
        wReserved3 = wReserved2 = wReserved1 = 0;
        val = ul;
        vt = VT_UI8;
    }
}