Abstract
In this contribution, we publish an algorithm for commercial (psychological) rounding. So what is that?
The problem: two ways of rounding
Rounding is a poorly understood art. As far as I can see, there are two ways of rounding:
1) "multiples of": lets say you want to round "123" Up to it´s nearest "50". That would be "150". But if you do the same for "176", it would be "200". Now, this is called "multiples off"-rounding: 200 is a multiple of 50, but.... it was not was I was looking for. It doesn´t END on "50", does it? I wanted something that always, always, calculated a price ENDING on something. Like .99, .98, .50, 50, 75, 100, whatever. Well, that´s another kind of rounding, and it is called
2) commercial (psychological) rounding: this kind of rounding gives you commercial (psychological) pricing. So, what´s the difference? Well, let´s do an example. Say we want to round 1.86 up to (a multiple of) 0.99. You would expect 1.99. But it gives you 1.98 (2 multiples of 0.99). It doesn´t end on .99, so that is not what we want.
Read on for the solution to this problem.
Caveat, user
This is new, and it is not trivial (well, at least not to me). I spent more time walking up and down the lawn frowning my face and thinking "How do I solve this" than I spent on coding it. It´s the first working version, I tested it and I think it works, but that´s what we all think, and it usually doesn´t. So if you find a glitch in the Matrix, please let me know, I´ll try to fix it (I am using it myself too and I can only benefit from it).
Code is documented, as best as I could, inline.
The code
Put the following code inside a Wix normal page. Run it, play with it and look at the Wix Console (or the browser´s console) for results. Good luck.
$w.onReady(function () {
console.log("1321 rounded Up on 75 = " + fnRoundAmount(1321, 75, 0));
console.log("1321 rounded Up on 99 = " + fnRoundAmount(1321, 99, 0));
console.log("1321 rounded Up on 98 = " + fnRoundAmount(1321, 98, 0));
console.log("1381 rounded Down on 100 = " + fnRoundAmount(1381, 100, 1));
console.log("1381 rounded Arith on 100 = " + fnRoundAmount(1381, 100, 2));
console.log("1321 rounded Down on 100 = " + fnRoundAmount(1321, 100, 1));
console.log("1321 rounded Arith on 100 = " + fnRoundAmount(1321, 100, 2));
console.log("187.50 rounded Up on 1 = " + fnRoundAmount(187.5, 1, 0));
console.log("187.50 rounded Down on 1 = " + fnRoundAmount(187.5, 1, 1));
console.log("187.50 rounded Arith on 1 = " + fnRoundAmount(187.5, 1, 2));
console.log("0.43 rounded Up on 0.99 = " + fnRoundAmount(0.43, 0.99, 0));
console.log("0.43 rounded Down on 0.99 = " + fnRoundAmount(0.43, 0.99, 1));
});
function fnRoundAmount(numAmountIn, numRoundOn, numRoundType) {
//Rounds a number Up, Down or Up/Down arithmetically on a certain amount. Call it like:
//fnRoundAmount(1321, 75, 0). It will return 1375
//numRoundOn may be greater than numAmountIn (like round 0.43 up to 0.99). Values for numRoundType
//are:0=Up,1=Down,2=arithmetically
let numDiv = numAmountIn / numRoundOn; //devide amount by number to round on. You will get some float
let numFactor = fnFindFactor (numRoundOn); // find nearest 1, 10, 100 etc to calculate back from
/*
Below a list of outcomes. The factor is the amount which, if we subtract (Factor-Roundon) from it, we get the correct
Rounding. Example : Round on 99, means Factor = 100. Later on, we do: 100-99=1. Then, after first Rounding to nearest Amnount, we subtract this
1 from the amount to come up with 99.
Round on Factor
500 1000
99 100
100 100
9 10
0.95 1
*/
let numMod = numAmountIn % numRoundOn; // calc modulo if we have to do Arithmatic rounding
let numModDivByUnit = numMod / numRoundOn; // devide mod by amount to round on
let boolIsgeHalf = numModDivByUnit >= 0.5; // is it greater or equal than 0.5? Then Method is up for arithmetical
let numBaseUp = Math.round(numDiv + 0.5); //round the division (amount/roundon) up for Up method
let numBaseDown = Math.round(numDiv - 0.5); // same for Down
/*
Here we do the real calculation. First, we round Up/Down to the nearest number where Number % Factor = 0 Example:
1321 Round up on 75 becomes 1400 (nearest) - (100 (Factor) -75 (RoundOn)) = 1400 - 25, thus 1375
*/
let numRoundedUp = fnRoundOnNearest(numBaseUp * numRoundOn, numFactor) - (numFactor - numRoundOn);
/*
Same for Down
1321 Round down on 75 becomes 1300 (nearest) - (100 (Factor) -75 (RoundOn)) = 1300 - 25, thus 1275
*/
let numRoundedDown = fnRoundOnNearest(numBaseDown * numRoundOn, numFactor) - (numFactor - numRoundOn);
switch (numRoundType) {
case 0: // up
return numRoundedUp;
case 1: // down
return numRoundedDown;
case 2: // arithmetical
if (boolIsgeHalf) {
return numRoundedUp;
} else return numRoundedDown;
}
}
function fnRoundOnNearest(numAmount, numFactor) {
// a provision for amounts like 0.49. Force at least 1 go inside the loop
if (numAmount < 1) {
numAmount = 1;
}
for (var i = numAmount; i <= numAmount + numFactor; i++) {
// find first number which, Modulo´d by Factor, returns 0
if (i % numFactor === 0) {
return i;
}
}
}
function fnFindFactor (numAmount) {
/*
The Factor is the nearest of a set of numbers which, when we subtract the rounding from the factor, and then subtract this
result from the amount, we get the correct rounding.
Example: 1321 Round up on 75. The factor for 75 = 100. Meaning: 100-75=25. When we subtract this number from the first
calculated Base amount (1400), we get 1400-25 = 1375.
*/
//create an array. You could do this in a loop, but this is easier to read and enough for me
// we simply compare the amount with each number as long as the amount is smaller or equal. Then we return the value as Factor
let arrLimits = [1,10,100,1000,10000,100000,1000000,10000000,100000000]
for (var i = 0; i < arrLimits.length; i++) {
if (numAmount <= arrLimits[i]) {
return arrLimits[i];
}
}
}
Comments