最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to increase the value of a number to the next multiple of 10, 100, 1000, 10,000 and so on - Stack Overflow

programmeradmin6浏览0评论

You'll have to forgive the phrasing of this question, I'm sure there's a better, more succinct way to ask it, but I don't know it.

Let's say I have a graph, and all the y-axis values are

[0,4,5,3,2,5,6]

The maximum value is six. So I would like the Y-Scale to be labeled from 0 to 10.

Given the following values

[33,26,54,23,86,23]

The maximum value is 86, so I would like the Y-Scale to go from 0 to 90.

Now let's say I have the following values

[98,253,87, 876,263]

The max is 876,so the Y-scale should go from 0 to 900

Now I have created the following function that should give me all the max y-scale values I need so far.

function padMaxValue(value){
        for(var i = 1; i < 1000000000000000000; i = i * 10){
            var decimalValue = value / i;

            if(value === i){
                return i;
            }

            if(decimalValue < 1 && decimalValue > 0.09){
                return i;
            }

        }
    }

However, given the following values

[99,123,82,189,45]

My function would set the y-scale max to 1000. But the max should really be 200. I realise that what I really need is a smarter way to increase the value of i instead of just multiplying it by 10. I need to be able to increase the value of i by 10, all the way up to 100. Then increase it by 100, all the way up to 1000. Then increase it by 1000, all the way up to 10,000 and so on.

I feel like there should be some neat and tidy mathematical way to do this. And I also feel that the 1000000000000000000 number I have in the for loop betrays my ignorance of mathematics.

Anyhoot, that's the problem. Any ideas?

You'll have to forgive the phrasing of this question, I'm sure there's a better, more succinct way to ask it, but I don't know it.

Let's say I have a graph, and all the y-axis values are

[0,4,5,3,2,5,6]

The maximum value is six. So I would like the Y-Scale to be labeled from 0 to 10.

Given the following values

[33,26,54,23,86,23]

The maximum value is 86, so I would like the Y-Scale to go from 0 to 90.

Now let's say I have the following values

[98,253,87, 876,263]

The max is 876,so the Y-scale should go from 0 to 900

Now I have created the following function that should give me all the max y-scale values I need so far.

function padMaxValue(value){
        for(var i = 1; i < 1000000000000000000; i = i * 10){
            var decimalValue = value / i;

            if(value === i){
                return i;
            }

            if(decimalValue < 1 && decimalValue > 0.09){
                return i;
            }

        }
    }

However, given the following values

[99,123,82,189,45]

My function would set the y-scale max to 1000. But the max should really be 200. I realise that what I really need is a smarter way to increase the value of i instead of just multiplying it by 10. I need to be able to increase the value of i by 10, all the way up to 100. Then increase it by 100, all the way up to 1000. Then increase it by 1000, all the way up to 10,000 and so on.

I feel like there should be some neat and tidy mathematical way to do this. And I also feel that the 1000000000000000000 number I have in the for loop betrays my ignorance of mathematics.

Anyhoot, that's the problem. Any ideas?

Share Improve this question edited Oct 31, 2014 at 10:48 gargantuan asked Oct 30, 2014 at 19:48 gargantuangargantuan 8,94616 gold badges69 silver badges110 bronze badges 7
  • You might consider two steps: 1. get min and max values of your data. 2. rounding them as you desire. – M. Page Commented Oct 30, 2014 at 19:54
  • 34 Why does 86 go to 100 not 90 if 876 goes to 900 not 1000? – curiousdannii Commented Oct 30, 2014 at 22:49
  • 4 good point. Just felt natural, humans are irrational. – gargantuan Commented Oct 30, 2014 at 22:56
  • 6 Do you want consistent rules or an exception for low values? Either way you should update your question to reflect your choice. All answers I have tested fails at your second test-case. – Hjulle Commented Oct 30, 2014 at 23:10
  • 4 The answers you have are good from an implementation/stackoverflow perspective, but you should probably also head over to the ux (User Experience) exchange to see if you really want to do this. My choice would be a small array of hand-picked, psychologically comforable, ux-driven values. This will be denser at the lower end of the scale than at the top end. You'd then pick the lowest of these, scaled by a power of 10, that can fit your data. – DeveloperInDevelopment Commented Oct 31, 2014 at 18:00
 |  Show 2 more comments

7 Answers 7

Reset to default 83

There is no need to go into the land of strings, which could be awkward if you ever had a decimal value.

function RoundedMax(a) {
    var mx = Math.max.apply(Math, a);
    if (mx == 0) {return 0};
    var size = Math.floor(Math.log(Math.abs(mx)) / Math.LN10);
    var magnitude = Math.pow(10, size);
    var yMax = Math.ceil(mx / magnitude) * magnitude;
    return yMax;
}

function RoundedMin(a) {
    var mn = Math.min.apply(Math, a);
    if (mn == 0) {return 0};
    var size = Math.floor(Math.log(Math.abs(mn)) / Math.LN10);
    var magnitude = Math.pow(10, size);
    var yMin = Math.floor(mn / magnitude) * magnitude;
    return yMin;
}

var arr = [-9.9,-1.23,-8.2,-2.01,-4.5,0];
document.write(RoundedMax(arr) + " " + RoundedMin(arr));

Outputs: 0 -10.

EDIT Updated in view of the comments. Now works even in IE8.


Now (2023) that all current browsers support ECMAScript 6:

function RoundedMax(a) {
    var mx = Math.max.apply(Math, a);
    if (mx == 0) {return 0};
    var size = Math.floor(Math.log10(Math.abs(mx)));
    var magnitude = Math.pow(10, size);
    var yMax = Math.ceil(mx / magnitude) * magnitude;
    return yMax;
}

function RoundedMin(a) {
    var mn = Math.min.apply(Math, a);
    if (mn == 0) {return 0};
    var size = Math.floor(Math.log10(Math.abs(mn)));
    var magnitude = Math.pow(10, size);
    var yMin = Math.floor(mn / magnitude) * magnitude;
    return yMin;
}

I don't like math, so here is a pretty simple/hilarious string-manipulation solution:

Find the maximum value:

var max = Math.max.apply(Math, [98,253,87, 876,263]); // 876

Take its first character

var c = max.toString()[0] // "8"

Make it an integer and add 1

c = (c | 0) + 1 // 9

Convert it back to a string:

c = c.toString() // "9"

Add N - 1 zeros to it, where N is the length of your original number:

c += Array(max.toString().length).join("0") // "900"

Convert it back to an integer:

c = (c | 0) // 900

Done!


Seriously though, use math.

I believe this would do the trick for you:

var data = [98, 253, 87, 876, 263, -155];
var max = Math.max.apply(null, data); // 
var factor = Math.pow(10, Math.floor(Math.log(Math.abs(max)) / Math.LN10)); 
if (factor == 0) { factor = 1; }                  
var magnitude = Math.ceil(max / (factor * 1.00)) * factor; 

Basically what's happening above is the following:

  1. Find the maximum of the sample
  2. Get the maximum values length, then raise it to a power of 10 i.e. 345, length = 3, so factor is 100
  3. Make sure we don't divide by 0
  4. Divide the maximum number by the factor to get a decimal, and take the ceiling of that number, then multiply it back by the factor to get the right number of 0s.

UPDATE: If you want to find the minimum value (for negative values), just flip the Math.ceil to Math.floor. You also have to take the absolute value of the minimum to make sure you don't count the negative character as part of the string.

var data = [98, 253, 87, 876, 263, -155];
var min = Math.min.apply(null, data);
var factor = Math.pow(10, Math.floor(Math.log(Math.abs(max)) / Math.LN10));
if (factor == 0) { factor = 1; }
var mag = Math.floor(min / (factor * 1.00)) * factor //  mag = -200;

UPDATE 2: As many people have said in the comments, we shouldn't be using string manipulation. I updated the code to use logarithms instead.

I wound up with:

function getGraphRange(data)
{
    var max=Math.max.apply(null, data);
    var min=Math.min.apply(null, data);
    var maxDigits=max.toString().length;
    var minDigits=min.toString().length;
    var maxD=Math.pow(10,Math.max((maxDigits-1),1));
    var minD=Math.pow(10,Math.max((minDigits-1),1));
    var maxR=(Math.ceil(max/maxD)*maxD);
    var minR=(Math.floor(min/minD)*minD);
    return [minR,maxR];
}
alert(getGraphRange([11, 20, 345, 99]).join(' - '));//10-400
alert(getGraphRange([0,4,5,3,2,5,6]).join(' - '));//0-10
alert(getGraphRange([98,253,87,876,263]).join(' - '));//80-900

http://jsfiddle.net/p4xjs9na/

There are already good answers here. I have used while loop to solve the problem. The function is very fast and it can return value in less than a second for even a very large number.

function padMaxValue(value)
{
    var i = 10;
    var counter = 0;
    var roundMax = 10;
    var copyValue = value;
    //If the value is negative, convert copyValue to positive
    if(value < 0) copyValue *= -1;
    while(roundMax <= copyValue)
    {
        roundMax += i;
        counter++;
        if(counter == 9)
        {
            i *= 10;
            counter = 0;
        }
    }
    if(value < 0) roundMax *= -1;
    return roundMax;
}
document.write(padMaxValue(8) + "<br>");
document.write(padMaxValue(77) + "<br>");
document.write(padMaxValue(889.65) + "<br>");
document.write(padMaxValue(-880.65) + "<br>");
document.write(padMaxValue(-765889.65) + "<br>");
document.write(padMaxValue(7888.88) + "<br>");
document.write(padMaxValue(88999887788899.099887) + "<br>");

Output:

10

80

900

-900

-800000

8000

90000000000000

I am not much of Math person but i take programming seriously! I came up with this solution!

function getMax(max){
    var mCopy = max;
    var d=1;//Assuming everything is greater than 10, 
    while(Math.floor(mCopy) > 10){//if the number is not one digited 
        mCopy /= Math.pow(10,d);
        d++;
    }
    d--;//back one digit
    if(d<1)
       d++;
    if((Math.floor(max / Math.pow(10,d))*Math.pow(10,d))==max)
       return max;
    return Math.floor(max / Math.pow(10,d))*Math.pow(10,d) + Math.pow(10,d);
}

Hope this helps

In my opinion the given answers are overly complicated. Here is an anonymous function that can be used in MATLAB:

next = @(x,n) ceil(x/n)*n;

>> next(11,10)    
ans =    
    20 %# 20 is the next "10" after "11")

>> next(110,100)    
ans =    
   200 %# 200 is the next "100" after "110"

>> next([98,253,87, 876,263],100)    
ans =    
   100   300   100   900   300

x is the number that should be processed and n is the requested step size.

edit Automation of determining the next order of magnitude is also possible

>> nextOrder = @(x) ceil(x/10.^ceil(log10(x))) * 10.^ceil(log10(x));

>> nextOrder([98,253,87,876,263])    
ans =    
         100        1000         100        1000        1000

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论