How to Deal With the Sum of Rounded Percentage Not Being 100

How to make rounded percentages add up to 100%

Since none of the answers here seem to solve it properly, here's my semi-obfuscated version using underscorejs:

function foo(l, target) {
var off = target - _.reduce(l, function(acc, x) { return acc + Math.round(x) }, 0);
return _.chain(l).
sortBy(function(x) { return Math.round(x) - x }).
map(function(x, i) { return Math.round(x) + (off > i) - (i >= (l.length + off)) }).
value();
}

foo([13.626332, 47.989636, 9.596008, 28.788024], 100) // => [48, 29, 14, 9]
foo([16.666, 16.666, 16.666, 16.666, 16.666, 16.666], 100) // => [17, 17, 17, 17, 16, 16]
foo([33.333, 33.333, 33.333], 100) // => [34, 33, 33]
foo([33.3, 33.3, 33.3, 0.1], 100) // => [34, 33, 33, 0]

How to deal with the sum of rounded percentage not being 100?

Option 1

If you are concerned about the results looking a bit strange to the user, I would put a footnote regarding the results mentioning that percentages have been rounded and may not total to 100%. You could programmatically display the message only when the rounding causes this behavior.

USA percentage:       43
Australia percentage: 29
Germany percentage: 29

*Percentages may not total 100 due to rounding

Option 2

Since you are using Ruby, I would suggest using rational numbers. This way you don't lose the precision when needed. Instead of the footnote, you might display the percentage with the rational numbers next to it like the following:

USA percentage:       43 (3/7)
Australia percentage: 29 (2/7)
Germany percentage: 29 (2/7)

Option 3

Include more decimal places so that the rounding error is less severe:

USA percentage:       42.9
Australia percentage: 28.6
Germany percentage: 28.6

This results in 100.1 instead of 101.

How to round-off on a set of numbers which sum to 100%

First, your analysis about the probable cause is not the actual cause here.
But the concept you're talking about is called bias in rounding schemes. It does exist and is real - it just isn't the cause of your specific issue here.

In the example you claim is causing this: 49.5% + 50.5% = 100%, rounded up, 50% + 51% = 101%.
Rounding up (rounding-towards-positive-infinity) is equivalent to round-away-from-zero for positive numbers. See the list of rounding-schemes below 1.
But again, that's pretty unlikely to be the cause here, unless you happened to get two adjacent numbers which were identically equal to a.5 and b.5 Not a.7 + b.8 + c.5, or any other combination of digits.
To prove why this is not the actual cause, in this list of n numbers, there are (n-1) adjacent pairs and if we make the reasonable assumption that every last digit is equally probable, then the chance of getting adjacent digits a.5, b.5 is only (0.1)^2 = 0.01

Anyway the real cause here is the numerical error introduced by missing precision (due to truncated representations of numbers converted to strings '%2.1f') (in whichever language they used, presumably PHP, Javascript or Java)...

The usual and simplest solution is to just carry more precision. Strictly you might only need one (or two) digits here, but IEEE 754 floats give you 23 digits of mantissa for free, so everyone uses that.

However if you really insist on setting yourself the (artificial) challenge of rounding numbers with missing precision and under the constraint that they must sum to 100.0% (or maximize the chance that they do), there are several lesser-used rounding schemes.
You can find these in textbooks and they aren't used much in the real world for obvious reasons, because they introduce randomness and possibly nondeterminism (although you could set the random seed, to at least ensure reproducibility).

So for whatever it's worth here are those rounding schemes (and many others, see the entire article):

[2] http://en.wikipedia.org/wiki/Rounding#Tie-breaking

The following all result in bias for the q=.5 case, and you said you want to avoid using them at all (instead of carrying extra precision, which makes the issue go away):

  • Round half up
  • Round half down
  • Round half away from zero
  • Round half towards zero
  • Round half to even
  • Round half to odd

Now here are the ones of interest to you:

  • Stochastic rounding:
    Another unbiased tie-breaking method is stochastic rounding:

If the fractional part of y is .5, choose q randomly among y + 0.5 and y − 0.5, with equal probability.
Advantages: essentially free of overall bias; but it is also 'fair' among even and odd q values. On the other hand, it introduces a random component into the result; performing the same computation twice on the same data may yield two different results. Also, it is open to nonconscious bias if humans (rather than computers or devices of chance) are "randomly" deciding in which direction to round.

  • Alternating tie-breaking:
    One method, more obscure than most, is round half alternatingly.

If the fractional part is 0.5, alternate round up and round down: for the first occurrence of a 0.5 fractional part, round up; for the second occurrence, round down; so on so forth.
This suppresses the random component of the result, if occurrences of 0.5 fractional parts can be effectively numbered. But it can still introduce a positive or negative bias according to the direction of rounding assigned to the first occurrence, if the total number of occurrences is odd.

If you want to read all about this stuff (computer arithmetic, and the hardware circuits that implement it), one good reference (that goes heavy on the hardware side) is

Computer Arithmetic Algorithms, 2nd Edition by Israel Koren
www.ecs.umass.edu/ece/koren/arith/‎
University of Massachusetts
Amherst, 2010

Percentages Not Adding Up to 100%

The issue is that you are rounding. Given that each number has a fraction that results in each being floored, you are losing said fractions in the end result. If you sum each of the fractions, you get 1. Which is what you're missing. This is generally how these types of charts are displayed.

However, if you are concerned about the totals not equaling 100% you may have to do some "fudging" of the results.
One way to do this would be to look at each fraction and increase the number with the largest fraction by 1 to account for this. You could also just increase the largest whole number by 1 as well, but may not be as accurate as the former. This would give you a result of 79%, 13%, and 8%, which equals 100.

Get 100% with rounded percentages of objects values in an array

One way to achieve this is to iterate over the array, maintaining a rolling sum of the rounded values and then setting the last value in the array to 100 - sum:

const items = [
{ name: 'Something', value: 20.4 },
{ name: 'something else', value: 36.6 },
{ name: 'another thing', value: 21.5 },
{ name: 'other item', value: 21.5 }
];

let sum = 0;
const rounded = items.map(({name, value}, i) => {
if (i == items.length - 1) {
return { name, value: 100 - sum };
}
value = Math.round(value);
sum = sum + value;
return { name, value };
});

console.log(rounded);

Sum of percent widths not rounded to 100%

Maybe with table display?

http://jsfiddle.net/CV6fp/6/

.CENTER {
max-width: 400px;
margin-left: auto;
margin-right: auto;
}

.BAR {
height: 10px;
display: table;
width: 100%;
}

.SEGMENT {
display: table-cell;
height: 100%;
}

.BAR { background-color: #F00; border: 1px solid #000; }

.SEGMENT.ONE { background-color: #FDA; }

.SEGMENT.TWO { background-color: #ADF; }


Related Topics



Leave a reply



Submit