Last active
August 29, 2015 13:59
-
-
Save niklas-r/10456375 to your computer and use it in GitHub Desktop.
Round list of floats to list of integers. The sum of the integers will be the same as the floats combined and then rounded.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function roundFloats(listOfFloats) { | |
var tempArr, | |
arraySum, | |
lowerSum, | |
largestFraction, | |
i; | |
// lowerSum will be the sum of all floats Math.floored | |
lowerSum = 0; | |
tempArr = listOfFloats.map(function (flp, i) { | |
var item; | |
// array items with higher individual difference are | |
// more likely to get an extra integer from the | |
// total differene sum. | |
item = { | |
result: Math.floor(flp), | |
difference: flp - Math.floor(flp), | |
index: i | |
}; | |
lowerSum += item.difference; | |
return item; | |
}); | |
// we need to know the largest fraction size because | |
// we have to multiply each array item by that | |
// in order to avoid weird float point bugs | |
largestFraction = listOfFloats.reduce(function (memo, fl) { | |
var fractionSize; | |
fractionSize = (fl + '').split('.')[1].length; | |
return (fractionSize > memo) ? fractionSize : memo; | |
}, 0); | |
// get sum of array | |
arraySum = | |
listOfFloats.reduce(function (memo, fl) { | |
return memo + (fl * largestFraction); | |
}, 0) / largestFraction; | |
// This does some magic. JavaScript uses the IEEE 754 standard for | |
// floating point values which causes all kinds of fun bugs. | |
// To mitigate this we set the lowerSum to only have 2 decimals at | |
// the start. toFixed returns a string so we later must parse it as | |
// a float again. In the end we run a Math.floor on the sum to eliminate | |
// any leftover decimal point. This should only matter if the sum of | |
// your original array isn't an integer. | |
lowerSum = Math.floor( | |
parseFloat( | |
lowerSum.toFixed(2) | |
) | |
); | |
// sort tempArr based on difference | |
tempArr.sort(function (a, b) { | |
if ( a.difference < b.difference ) { | |
return -1; | |
} | |
if ( a.difference > b.difference ) { | |
return 1; | |
} | |
return 0; | |
}).reverse(); | |
// Add 1 to those most likely to round up to the | |
// next number so that the difference is nullified. | |
i = 0; | |
while (lowerSum) { | |
tempArr[i].result += 1; | |
lowerSum--; | |
i++; | |
} | |
// sort tempArr back to original index | |
tempArr.sort(function (a, b) { | |
return a.index - b.index; | |
}); | |
// return array of results | |
return tempArr.map(function (item) { return item.result; }); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
describe("roundFloats", function() { | |
it('should round lists of floats to int', function () { | |
var test = [ | |
{ | |
unprocessed: [0.02, 0.03, 0.05, 0.06, 0.07, 0.08, | |
0.09, 0.1, 0.11, 0.12, 0.13, 0.14], // = 1 | |
processed: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1] // = 1 | |
}, | |
{ | |
unprocessed: [0.1, 0.3, 0.4, 0.4, 0.8], // = 2 | |
processed: [0, 0, 0, 1, 1] // = 2 | |
}, | |
{ | |
unprocessed: [0.1, 0.1, 0.1, 0.1, 0.1, | |
0.1, 0.1, 0.1, 0.1, 0.1], // = 0.9999999999999999 | |
processed: [0, 0, 0, 0, 0, 0, 0, 0, 0, 1] // = 1 | |
}, | |
{ | |
unprocessed: [0.4, 0.4, 0.4, 0.4, 9.2, 9.2], // = 20 | |
processed: [0, 0, 1, 1, 9, 9] // = 20 | |
}, | |
{ | |
unprocessed: [0.5, 0.5, 11], // = 12 | |
processed: [0, 1, 11] // = 12 | |
}, | |
{ | |
unprocessed: [45.5, 54.5], // = 100 | |
processed: [45, 55] // = 100 | |
}, | |
{ | |
unprocessed: [0.8, 1.3], // = 2.1 | |
processed: [1, 1] // = 2 | |
}, | |
{ | |
unprocessed: [2.8, 4.7, 7.8, 10.9, 5.6], // = 31.800000000000004 | |
processed: [3, 4, 8, 11, 5] // = 31 | |
}, | |
{ | |
unprocessed: [0.99, 0.51, 0.34, 0.44, 0.8, 0.91], // = 3.99 | |
processed: [1, 0, 0, 0, 1, 1] // = 3 | |
}, | |
{ | |
unprocessed: [33.333333, 33.333333, 33.333333], // = 99.999999 | |
processed: [33, 33, 34] // = 100 | |
}, | |
{ | |
unprocessed: [100], | |
processed: [100] | |
}, | |
{ | |
unprocessed: [], | |
processed: [] | |
} | |
]; | |
for (var i = 0; i < test.length; i++) { | |
expect( | |
roundFloats(test[i].unprocessed) | |
).toEqual( | |
test[i].processed | |
); | |
}; | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment