round to nearest nice number

后端 未结 3 648
闹比i
闹比i 2021-02-06 11:05

I\'m writing an application which requires rounding labels to the nearest \'nice\' number. I\'ll put some code below to demonstrate this, but my issue is that I was using a ser

相关标签:
3条回答
  • 2021-02-06 11:47

    I prefer the following over Groo's approach, as it rounds 267 to 275 instead of 500. It basically rounds to the first digit, and then the nearest quarter fraction of that power of 10.

    static double round_pretty(double val) {
        var fraction = 1;
        var log = Math.floor(Math.log10(val));
    
        // This keeps from adding digits after the decimal
        if(log > 1) {
            fraction = 4;
        }
    
        return Math.round(val * fraction * Math.pow(10, -log)) 
            / fraction / Math.pow(10, -log);
    }
    

    The output is as follows:

    0.01 -> 0.01
    0.025 -> 0.03    (Groo's does 0.025)
    0.1 -> 0.1
    0.2 -> 0.2       (Groo's does 0.25)
    0.49 -> 0.5
    0.5 -> 0.5       (Groo's does 1)
    0.51 -> 0.5      (Groo's does 1)
    0.7 -> 0.7       (Groo's does 1)
    1 -> 1
    2 -> 2           (Groo's does 2.5)
    9 -> 9
    23.07 -> 20
    25 -> 30
    49 -> 50
    50 -> 50         (Groo's does 100 here)
    58 -> 60
    94 -> 90
    95 -> 100
    99 -> 100
    100 -> 100
    109 -> 100       (Groo's does 250 here)
    158 -> 150
    249 -> 250
    267 -> 275
    832 -> 825
    1234567 -> 1250000
    1499999 -> 1500000
    1625000 -> 1750000
    
    0 讨论(0)
  • 2021-02-06 11:59

    I came up with this quite crude solution, which returns the correct values for all of the cases I tested just now:

    public static double foo(long value) {
        for (double i = 0.2; true; i *= 5) {
            if (i >= value) {
                return i / 5;
            }
        }
    }
    

    Although I must admit that a mathematical solution as posted by Groo would be more beautiful. ;)

    0 讨论(0)
  • 2021-02-06 12:05

    You can use Math.log10 to normalize all values before doing your "nice number" search, something like this:

    [Edit] I just realized you are using Java instead of C#, so I modified the code a bit. I don't have a compiler around to test it, but you should get the general idea anyway:

    static double getNicerNumber(double val)
    {
        // get the first larger power of 10
        var nice = Math.pow(10, Math.ceiling(Math.log10(val)));
    
        // scale the power to a "nice enough" value
        if (val < 0.25 * nice)
            nice = 0.25 * nice;
        else if (val < 0.5 * nice)
            nice = 0.5 * nice;
    
        return nice;
    }
    
    // test program:
    static void main(string[] args)
    {
        double[] values = 
        {
            0.1, 0.2, 0.7,
            1, 2, 9,
            25, 58, 99,
            158, 267, 832
        };
    
        for (var val : values)
            System.out.printf("$%.2f --> $%.2f%n", val, getNicerNumber(val));
    }
    

    This will print something like:

    0,1 --> 0,1
    0,2 --> 0,25
    0,7 --> 1
    1 --> 1
    2 --> 2,5
    9 --> 10
    25 --> 50
    58 --> 100
    99 --> 100
    158 --> 250
    267 --> 500
    832 --> 1000
    0 讨论(0)
提交回复
热议问题