Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Make big and small numbers human-readable [duplicate]

I would like to print my very small numbers in C# in a human friendly way, such as:

30µ for 3E-5 or 456.789n for 0.000000456789.

I know of the Humanize_number() function from BSD in C, but only compatible with bit ints, not floats and doubles. Is there the equivalent in C# that supports those?

Also, it should keep a certain amount of precision when displaying numbers, like:

0.003596 should be displayed as 3.596µ, not 3.6µ (or worse, ).

The possible answer here: Formatting Large Numbers with .NET but adapted for negative log10 is truncating the numbers to 1 digit after the comma. That's far from complete in my opinion.

Examples of how I'd like to present things:

3000        3K
3300        3.3K
3333        3.333K
30000       30k
300000      300k
3000000     3M
3000003     3.000003M // or 3M if I specify "4 digits precision"
0.253       253m
0.0253      25.3m
0.00253     2.53m
-0.253003   -253.003m

I couldn't formulate my question to find relevant answers in SO, so if the question has been already answered, fire away!

like image 383
Gui13 Avatar asked Apr 18 '13 13:04

Gui13


2 Answers

Try this:

static class Extensions
{
    static string[] prefixes= { "f", "a", "p", "n", "μ", "m", string.Empty, "k", "M", "G", "T", "P", "E" };

    public static string Nice(this double x, int significant_digits)
    {
        //Check for special numbers and non-numbers
        if(double.IsInfinity(x)||double.IsNaN(x)||x==0||significant_digits<=0)
        {
            return x.ToString();
        }
        // extract sign so we deal with positive numbers only
        int sign=Math.Sign(x);
        x=Math.Abs(x);
        // get scientific exponent, 10^3, 10^6, ...
        int sci= x==0? 0 : (int)Math.Floor(Math.Log(x, 10)/3)*3;
        // scale number to exponent found
        x=x*Math.Pow(10, -sci);
        // find number of digits to the left of the decimal
        int dg= x==0? 0 : (int)Math.Floor(Math.Log(x, 10))+1;
        // adjust decimals to display
        int decimals=Math.Min(significant_digits-dg, 15);
        // format for the decimals
        string fmt=new string('0', decimals);
        if(sci==0)
        {
            //no exponent
            return string.Format("{0}{1:0."+fmt+"}",
                sign<0?"-":string.Empty,
                Math.Round(x, decimals));
        }
        // find index for prefix. every 3 of sci is a new index
        int index=sci/3+6;
        if(index>=0&&index<prefixes.Length)
        {
            // with prefix
            return string.Format("{0}{1:0."+fmt+"}{2}",
                sign<0?"-":string.Empty,
                Math.Round(x, decimals),
                prefixes[index]);
        }
        // with 10^exp format
        return string.Format("{0}{1:0."+fmt+"}·10^{2}",
            sign<0?"-":string.Empty,
            Math.Round(x, decimals),
            sci);
    }

    // test code
    static void Main(string[] args)
    {
        double x=Math.PI/10e20;
        do
        {
            Console.WriteLine(string.Format( "\t{0,20} = {1}", x, x.Nice(4)));
            x*=10;
        } while(x<=Math.PI*10e20);
    }
}

Test output with four significant digits:

    3.14159265358979E-19 = 314.2·10^-2
     1.5707963267949E-18 = 1.571f
    7.85398163397448E-18 = 7.854f
    3.92699081698724E-17 = 39.27f
    1.96349540849362E-16 = 196.3f
     9.8174770424681E-16 = 981.7f
    4.90873852123405E-15 = 4.909a
    2.45436926061703E-14 = 24.54a
    1.22718463030851E-13 = 122.7a
    6.13592315154256E-13 = 613.6a
    3.06796157577128E-12 = 3.068p
    1.53398078788564E-11 = 15.34p
     7.6699039394282E-11 = 76.70p
     3.8349519697141E-10 = 383.5p
    1.91747598485705E-09 = 1.917n
    9.58737992428526E-09 = 9.587n
    4.79368996214263E-08 = 47.94n
    2.39684498107131E-07 = 239.7n
    1.19842249053566E-06 = 1.198µ
    5.99211245267829E-06 = 5.992µ
    2.99605622633914E-05 = 29.96µ
    0.000149802811316957 = 149.8µ
    0.000749014056584786 = 749.0µ
     0.00374507028292393 = 3.745m
      0.0187253514146196 = 18.73m
      0.0936267570730982 = 93.63m
       0.468133785365491 = 468.1m
        2.34066892682745 = 2.341
        11.7033446341373 = 11.70
        58.5167231706864 = 58.52
        292.583615853432 = 292.6
        1462.91807926716 = 1.463k
         7314.5903963358 = 7.315k
         36572.951981679 = 36.57k
        182864.759908395 = 182.9k
        914323.799541975 = 914.3k
        4571618.99770987 = 4.572M
        22858094.9885494 = 22.86M
        114290474.942747 = 114.3M
        571452374.713734 = 571.5M
        2857261873.56867 = 2.857G
        14286309367.8434 = 14.29G
        71431546839.2168 = 71.43G
        357157734196.084 = 357.2G
        1785788670980.42 = 1.786T
         8928943354902.1 = 8.929T
        44644716774510.5 = 44.64T
         223223583872552 = 223.2T
    1.11611791936276E+15 = 1.116P
    5.58058959681381E+15 = 5.581P
    2.79029479840691E+16 = 27.90P
    1.39514739920345E+17 = 139.5P
    6.97573699601726E+17 = 697.6P
    3.48786849800863E+18 = 3.488E
    1.74393424900432E+19 = 17.44E
    8.71967124502158E+19 = 87.20E
    4.35983562251079E+20 = 436.0E
     2.1799178112554E+21 = 2.180·10^21 
like image 60
John Alexiou Avatar answered Nov 17 '22 11:11

John Alexiou


as you want the decimal to be displayed as sign and not as a lot of 0's you could as well do something like:

class Program
{
    static void Main(string[] args)
    {
        //these are your "unit precedessors"
        char[] exponentsbig = new char[] {' ', 'k', 'M', 'G', 'T', 'P', 'E' };
        char[] exponentssmall = new char[] { ' ', 'm', 'µ', 'n', 'p', 'a', 'f' };

        //some example numbers
        long[] numbersBig = new long[] { 3000, 3003, 30000, 300000, 300003, 1594900000000000 };
        double[] numbersSmall = new double[] { 0.0002, 0.245, 0.245003, 0.000004578 };
        //some helper vars
        int counter = 0;
        bool edited = false;
        //let's have a look at what we produce;)
        string output = "";

        //Big  numbers incoming!!
        for (int i = 0; i < numbersBig.Length; i++)
        {
            counter=0;
            double myNumber = Convert.ToDouble(numbersBig[i]);
            do
            {
                edited = false;
                //something to prevent unnecessary unit-adding and making sure you still divide by 1000
                if (myNumber/1000>1 )
                {
                    counter++;
                    myNumber /= 1000;
                    edited = true;
                }
            } while (edited);
            output += numbersBig[i] + " " + myNumber + exponentsbig[counter] + "\n";
        }

        //small  numbers incoming!!
        for (int i = 0; i < numbersSmall.Length; i++)
        {
            counter = 0;
            double myNumber = numbersSmall[i];
            do
            {
                edited = false;
                //this will go to 3 digits after comma. you can make the compared smaller 
                //to be more exact after the comma, but keep in mind you lose steps then
                if (myNumber < 1)
                {
                    counter++;
                    myNumber *= 1000;
                    edited = true;
                }
            } while (edited);
            output += numbersSmall[i] + " " + myNumber + exponentssmall[counter] + "\n";
        }
        //see what we did
        Console.Write(output);
        Console.ReadKey();

    }
}
like image 29
Vogel612 Avatar answered Nov 17 '22 09:11

Vogel612