Divide Number With Decimals Javascript
Solution 1:
review of other answers
After re-examining the solutions provided here, I noticed that they produce some strange results.
@GeorgeMauler's divideCurrencyEqually
appears to work with the inputs provided in the original question. But if you change them up a bit, it produces a very strange result for …
// it appears to work
divideCurrencyEvenly(10,3)
=> [ '3.33', '3.33', '3.34' ]
// wups ...
divideCurrencyEvenly(10,6)
=> [ '1.67', '1.67', '1.67', '1.67', '3.32' ]
// hmm this one works ...
divideCurrencyEvenly(18,5)
=> [ '3.60', '3.60', '3.60', '3.60', '3.60' ]
// wups ...
divideCurrencyEvenly(100,7)
=> [ '14.29', '14.29', '14.29', '14.29', '14.29', '28.55' ]
While @Barmar's solution seems to perform a bit better … well a little bit …
// it appears to work
divide(10,3)
=> [ '3.33', '3.33', 3.34 ]
// acceptable, i guess
divide(10,6)
=> [ '1.66', '1.66', '1.66', '1.66', '1.66', 1.700000000000001 ]
// hmm, not so good, but still acceptable
divide(18,5)
=> [ '3.60', '3.60', '3.60', '3.60', 3.5999999999999996 ]
// as acceptable as the last two, i guess
divide(100,7)
=> [ '14.28', '14.28', '14.28', '14.28', '14.28', '14.28', 14.320000000000007 ]
Numbers like 3.5999999999999996
are mostly forgivable because that's how float arithmetic works in JavaScript.
But, what I find most disturbing about divide
is that it's giving a mixed-type array (string and float). And because the numbers aren't rounded (to the correct precision) in the output, if you were to add the result up on paper, you will not arrive back at your original numerator
input — that is, unless you do the rounding on your result.
and we're still fighting for equality …
My last grievance exists for both of the above solutions too. The result does not represent a distribution that is as close to equal as is possible.
If you divide 100 by 7 with a precision of 2, @Barmar's solution (with the rounding problem fixed) would give …
[ '14.28', '14.28', '14.28', '14.28', '14.28', '14.28', 14.32 ]
If these were people and the numbers represented monies, 1 person shouldn't pay 4 pennies more. Instead 4 people should pay 1 penny more …
[ 14.29, 14.29, 14.29, 14.29, 14.28, 14.28, 14.28 ]
That's as close to equal as is possible
back to square one
I was dissatisfied with the solutions, so I made one of my own, distribute
.
It has 3 parameters: precisionp
, divisord
, and numeratorn
.
- it's returns a homogenous array — always an Array of Number
- if you add them up, you will get a value exactly equal to your original numerator
It scales the numerator and finds the largest integer q
where q * d <= n
. Using modular division we know how many "slices" need to contribute q+1
. Lastly, each q
or q+1
is scaled back down and populates the output array.
constfill = (n, x) =>
Array (n) .fill (x)
constconcat = (xs, ys) =>
xs .concat (ys)
constquotrem = (n, d) =>
[ Math .floor (n / d)
, Math .floor (n % d)
]
constdistribute = (p, d, n) =>
{ const e =
Math .pow (10, p)
const [ q, r ] =
quotrem (n * e, d)
return concat
( fill (r, (q + 1) / e)
, fill (d - r, q / e)
)
}
console .log
( distribute (2, 3, 10)
// [ 3.34, 3.33, 3.33 ]
, distribute (2, 6, 10)
// [ 1.67, 1.67, 1.67, 1.67, 1.66, 1.66 ]
, distribute (2, 5, 18)
// [ 3.6, 3.6, 3.6, 3.6, 3.6 ]
, distribute (2, 7, 100)
// [ 14.29, 14.29, 14.29, 14.29, 14.28, 14.28, 14.28 ]
)
You'll see that I made precision a parameter, p
, which means you can control how many decimal places come out. Also note how the largest difference Δ
between any number in the result is Δ <= 1/10^p
distribute (0, 7, 100)
=> [ 15, 15, 14, 14, 14, 14, 14 ] // Δ = 1distribute (1, 7, 100)
=> [ 14.3, 14.3, 14.3, 14.3, 14.3, 14.3, 14.2 ] // Δ = 0.1distribute (2, 7, 100)
=> [ 14.29, 14.29, 14.29, 14.29, 14.28, 14.28, 14.28 ] // Δ = 0.01distribute (3, 7, 100)
=> [ 14.286, 14.286, 14.286, 14.286, 14.286, 14.285, 14.285 ] // Δ = 0.001distribute (4, 7, 100)
=> [ 14.2858, 14.2857, 14.2857, 14.2857, 14.2857, 14.2857, 14.2857 ] // Δ = 0.0001
distribute
can be partially applied in meaningful ways. Here's one way petty people could use it to precisely split the bill at a restaurant …
// splitTheBill will use precision of 2 which works nice for moniesconstsplitTheBill = (people, money) =>
distribute (2, people, money)
// partyOfThree splits the bill between 3 peopleconstpartyOfThree = money =>
splitTheBill (3, money)
// how much does each person pay ?
partyOfThree (67.89)
=> [ 18.93, 18.93, 18.92 ]
And here's an effective way to divide people into groups — while being careful not to divide an individual person — which typically results in death …
// p=0 will yield only whole numbers in the resultconstmakeTeams = (teams, people) =>
distribute (0, teams, people)
// make 4 teams from 18 people// how many people on each team?
makeTeams (4, 18)
=> [ 5, 5, 4, 4 ]
Solution 2:
functiondivide(numerator, divisor) {
var results = [];
var dividend = (Math.floor(numerator/divisor*100)/100).toFixed(2); // dividend with 2 decimal placesfor (var i = 0; i < divisor-1; i++) {
results.push(dividend); // Put n-1 copies of dividend in results
}
results.push(numerator - (divisor-1)*dividend); // Add remainder to resultsreturn results;
}
Solution 3:
Sounds like a pretty straightforward loop/recursion.
function divideEvenly(numerator, minPartSize) {
if(numerator / minPartSize< 2) {
return [numerator];
}
return [minPartSize].concat(divideEvenly(numerator-minPartSize, minPartSize));
}
console.log(divideEvenly(1000, 333));
To get it to be two decimals of currency multiply both numbers by 100 before calling this function then divide each result by 100 and call toFixed(2)
.
functiondivideCurrencyEvenly(numerator, divisor) {
var minPartSize = +(numerator / divisor).toFixed(2)
return divideEvenly(numerator*100, minPartSize*100).map(function(v) {
return (v/100).toFixed(2);
});
}
console.log(divideCurrencyEvenly(3856, 3));
//=>["1285.33", "1285.33", "1285.34"]
Solution 4:
Since we are talking about money, normally a few cents different doesn't matter as long as the total adds up correctly. So if you don't mind one payment possibly being a few cents greater than the rest, here is a really simple solution for installment payments.
functionCalculateInstallments(total, installments) {
// Calculate Installment Paymentsvar pennies = total * 100;
var remainder = pennies % installments;
var otherPayments = (pennies - remainder) / installments;
var firstPayment = otherPayments + remainder;
for (var i = 0; i < installments; i++) {
if (i == 0) {
console.log("first payment = ", firstPayment / 100);
} else {
console.log("next payment = ", otherPayments / 100);
}
}
}
Since you said you wanted the highest payment at the end you would want to modify the if statement in the for loop: if (i==installments-1) { //last payment }
Solution 5:
There is an issue on @user633183's distribute
but only happen when the divider is lower than 3.
distribute(2, 2, 560.3)
// [ '280.15' , '280.14']
distribute(2, 1, 74.10)
// [ '74.09' ]
distribute(2, 1, 74.60)
// [ '74.59' ]
I rewrote the answer by Guffa into javascript
constdistribute = (precision, divider, numerator) => {
const arr = [];
while (divider > 0) {
let amount = Math.round((numerator / divider) * Math.pow(10, precision)) / Math.pow(10, precision);
arr.push(amount);
numerator -= amount;
divider--;
}
return arr.sort((a, b) => b-a);
};
Here are the results
distribute(0, 7, 100)
=> [ 15, 15, 14, 14, 14, 14, 14 ]
distribute(1, 7, 100)
=> [ 14.3, 14.3, 14.3, 14.3, 14.3, 14.3, 14.2 ]
distribute(2, 7, 100)
=> [ 14.29, 14.29, 14.29, 14.29, 14.28, 14.28, 14.28 ]
distribute(3, 7, 100)
=> [ 14.286, 14.286, 14.286, 14.286, 14.286, 14.285, 14.285 ]
distribute(4, 7, 100)
=> [ 14.2858, 14.2857, 14.2857, 14.2857, 14.2857, 14.2857, 14.2857 ]
// and of coursedistribute(2, 2, 560.3)
=> [ 280.15, 280.15 ]
distribute(2, 1, 74.10)
=> [ 74.1 ]
distribute(2, 1, 74.60)
=> [ 74.6 ]
Post a Comment for "Divide Number With Decimals Javascript"