Search code examples
javascriptmathpseudocode

How to calculate relative percentage (apply score to a new range)?


I'm a front-end and back-end developer, and something that I've come across a few times when dealing with simple game development and animation is this challenge. I wish I had a function to solve for this, so I was hoping you math guys/gals could help me out with the pseudocode, and then I could post a function that we can run via the browser to test it.

It's a little hard for me to explain the challenge, so forgive me, I'm going to give it out like a riddle:

You're trying to find your test score as a percent. You know that for a normal percent, expressed as a decimal from 0.00 - 1.00, you'd do something like actual_test_score / max_test_score.

However, you're not looking for any old percent. You'd like to express your percent as a decimal within a specified range of decimal numbers. For example, you'd like (given an actual_test_score and a max_test_score) to get your test score as a percent from 0.50 - 1.00.

How can you calculate actual_test_score / max_test_score expressed as a percentage between a range of 0.50 - 1.00, instead of your typical range that is 0.00 - 1.00.

It took kind of a while to explain this (and it might not be the best explanation) because it's not something I have to explain often, but again it's something I run into every now and then.


Solution

  • This is a linear range mapping from [0,1] to [newMin,newMax], which is a specific case of the general linear range mapping from [oldMin,oldMax] to [newMin,newMax]:

    function linearRemap(value, oldMin, oldMax, newMin, newMax) {
        var newScale = newMax - newMin;
        var valueAsPct = (val - oldMin) / (oldMax - oldMin);
        var scaledValue = valueAsPct * newScale;
        var shiftedAndScaledValue = scaledValue + newMin;
        return shiftedAndScaledValue;
    }
    

    So the limited case here permits you to skip the percent calculation line:

    function linearRemapPct(valueAsPct, newMin, newMax) {
        var newScale = newMax - newMin;
        var scaledValue = valueAsPct * newScale;
        var shiftedAndScaledValue = scaledValue + newMin;
        return shiftedAndScaledValue;
    }
    

    Or just

    function linearRemapPct(valueAsPct, newMin, newMax) {
        return (valueAsPct * (newMax - newMin) + newMin);
    }
    

    If you want to have something that's a bit more careful about loss of precision:

    function linearRemapPct(valueAsPct, newMin, newMax) {
        return (valueAsPct * newMax) + (1 - valueAsPct) * newMin;
    }