0

I have the following array:

var arr = [1, 2, 3, 8];

What I need to do is compute the average of each, but I have to first add them all up which I do in the following way:

var total = 0;
var fractionSum = 0;
var fractionArray = [];

for (var x = 0; x < arr.length; x++) {
   total += arr[x]
}

Then I compute the average:

for (var y = 0; y < arr.length; y++) {
   var average = (arr[y] / total) * 100;                                
   var averageFixed = average.toFixed(2);

   fractionArray.push(eval(averageFixed));
   fractionSum += fractionArray[y];
}

The problem I'm having is that the values in fractionArray are [57.14, 21.43, 14.29, 7.14] and when you add them outside of a javascript interpreter what you get is 100 (which is the desired result in this context), but the value I get in fractionSum is 99.99999999999999.

How can I fix this so I can get the "true result" of 100? I'm not interested in knowing "why" I'm getting 99.99999999999999 (which is answered here Is floating point math broken?) . Rather, my interest is in knowing what else do I need to add to the code so that instead of 78.57 + 14.29 equaling 92.85999999999999 (which happens on the third iteration of the arr array on this line fractionSum += fractionArray[y];) I get it to equal 92.86 and so on and so forth.

charlitos
  • 105
  • 2
  • 10
  • 2
    You can just `return Number(fractionSum.toFixed(2))` or shorthand `return +fractionSum.toFixed(2)`. Also, just use `Number()`. Don't ever use `eval()` for parsing numerical values from strings. – Patrick Roberts Jan 29 '18 at 21:56
  • `fractionSum = Math.round(fractionSum + fractionArray[y], 2);` – Jaromanda X Jan 29 '18 at 21:57
  • Thank you @PatrickRoberts for that recommendation! – charlitos Jan 29 '18 at 22:09
  • I wonder why are you _computing_ "fractionSum" at all? The sum of percentages will be 100 anyways. – georg Jan 29 '18 at 22:44
  • Good question @georg. Actually, it is not always equal 100. If arr = [1,1,1] fractionArray would be [33.33, 33.33, 33.33]. The cool part is that when I add these the fractionSum is 99.99. But when I use the original arr of [1, 2, 3, 8] fractionArray is [57.14, 21.43, 14.29, 7.14] and fractionSum is 99.99999999999999 . Because of this, I have to always check whether fractionSum is < or > than 100 and adjust accordingly because as you know averages have to always equal 100. – charlitos Jan 29 '18 at 23:07
  • Because your numbers are truncated, the output is not exactly the one you want. You can't do it this way. Why do you need to do this? – Sylvan LE DEUNFF Jan 29 '18 at 23:17
  • @SylvanLEDEUNFF, each value in the arr array represents a value of a pie chart. I have to display each value as a percentage and as an absolute value. For example, with arr [1,1,1] each slice of the pie chart would display 1 as absolute value and 33.33 as percentage. One of the percentages I have to display as 33.34 so that the sums of the percentages equal 100 (33.34 + 33.33 + 33.33 = 100), which means I have to add 0.01 to it. Sometimes the sum equals to 100.01 which means I have to subtract 0.01 from it. – charlitos Jan 30 '18 at 03:55
  • And why do you need to round values to 2 dp? I guess that you are displaying the value somewhere on the chart? If so, what framework are you using? – Sylvan LE DEUNFF Jan 30 '18 at 08:36

2 Answers2

0

First add up all values, and then round it to your required precision once you're done:

var myResult = 99.99999999999999;
console.log(myResult);      // 99.99999999999999

var roundedResult = Math.round(myResult);
console.log(roundedResult); // 100
TimoStaudinger
  • 41,396
  • 16
  • 88
  • 94
  • Uh... [`Math.round()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round) doesn't have a second argument... where did you ever get that idea? In your answer, `Math.round()` just discards the `2` and rounds it to an integer. – Patrick Roberts Jan 29 '18 at 21:59
  • @PatrickRoberts Hmm... good question, some other library probably – don't use it a lot. Thanks for the hint! – TimoStaudinger Jan 29 '18 at 22:01
  • 2
    But that method still doesn't help with OP's question - he wants precision up to two decimal places. The fact that the example at the top happens to add up to 100 is irrelevant, the example at the bottom lists `78.57 + 14.29` with `92.86` as desired result. – VLAZ Jan 29 '18 at 22:12
0

The fact that you are using decimal numbers says, that you don't need precission. Some fractions can not be exactly represented as decimal numbers.

E.g. in decimal you could write 1/3 as 0.33333. But 3 * 0.33333 = 0.99999, which is not 1.

I recommend to use fractions instead. E.g. you can represent one third as [1,3].

// add([1,2],[1,3]) = [5,6] 
function add(a,b) {  return [a[0]*b[1] + a[1]*b[0], a[1]*b[1]];  }

// sub([1,2],[1,3]) = [1,6] 
function sub(a,b) {  return add(a,[-b[0],b[1]]);  }

// mul([1,2],[1,3]) = [1,6] 
function mul(a,b) {  return [a[0]*b[0], a[1]*b[1]];  }

// div([3,1],[5,1]) = [3,5] 
function div(a,b) {  return mul(a, [b[1],b[0]]);  }

// equals([1,3],[3,9]) = true
function equals(a,b) {  return a[0]*b[1] == a[1]*b[0];  }

// toDecimal([1,3]) = 0.333333333
function toDecimal(a) {  return a[0]/a[1];  }
Ivan Kuckir
  • 2,327
  • 3
  • 27
  • 46