Round a Python list of numbers and maintain their sum Round a Python list of numbers and maintain their sum numpy numpy

Round a Python list of numbers and maintain their sum


The first step is to calculate the error between the desired result and the actual sum:

>>> error = originalTotal - sum(myRoundedList)>>> error0.01999999996041879

This can be either positive or negative. Since every item in myRoundedList is within 0.005 of the actual value, this error will be less than 0.01 per item of the original array. You can simply divide by 0.01 and round to get the number of items that must be adjusted:

>>> n = int(round(error / 0.01))>>> n2

Now all that's left is to select the items that should be adjusted. The optimal results come from adjusting those values that were closest to the boundary in the first place. You can find those by sorting by the difference between the original value and the rounded value.

>>> myNewList = myRoundedList[:]>>> for _,i in sorted(((myOriginalList[i] - myRoundedList[i], i) for i in range(len(myOriginalList))), reverse=n>0)[:abs(n)]:    myNewList[i] += math.copysign(0.01, n)>>> myRoundedList[27226.95, 193.06, 1764.31, 12625.86, 26714.68, 18970.35, 12725.41, 23589.93, 27948.4, 23767.83, 12449.81]>>> myNewList[27226.95, 193.06, 1764.31, 12625.86, 26714.68, 18970.359999999997, 12725.42, 23589.93, 27948.4, 23767.83, 12449.81]>>> sum(myNewList)187976.61


As noted in an answer by kettlehell, consider the PyPI package iteround. It does not internally use NumPy, however.

>>> from iteround import saferound>>> saferound([1.0, 2.1, 3.6], places=0)[1.0, 2.0, 4.0]


First of all you shouldn't use floats for storing money (use Decimals instead). But below I provide some quite generic solution - you need to store, accumulate and use the sum of differences in rounding. Some verbose (and not very pythonic ;-) example with your numbers:

# define your accuracydecimal_positions = 2numbers = [27226.94982, 193.0595233, 1764.3094, 12625.8607, 26714.67907, 18970.35388, 12725.41407, 23589.93271, 27948.40386, 23767.83261, 12449.81318]print round(sum(numbers),decimal_positions)>>> 187976.61new_numbers = list()rest = 0.0for n in numbers:    new_n = round(n + rest,decimal_positions)    rest += n - new_n    new_numbers.append( new_n )print sum(new_numbers)>>> 187976.61