1

(I'm not a JS developer!) I have an array of scores that I multiply by a standard set of weightings, to obtain a weighted overall score. I then want to express that to one decimal place.

After abandoning the battle with precision error when calculating with raw decimals, I took the approach to first multiply the weightings by 100. This is all working, except for this bizarre behaviour where 2.55 is rounded to 2.5 instead of 2.6! WTF?

No doubt this is JS 101 but can someone enlighten me as to WHY and how to address this?

weightings = [0.15, 0.10, 0.10, 0.15, 0.15, 0.25, 0.10];
scores1 = [2,2,2,2,2,3,2];
scores2 = [2,2,2,2,2,3,3];
scores3 = [3,3,1,2,3,3,2];

adjWeightings = weightings.map(x => x * 100); // get rid of decimals altogether

weightedResult1 = scores1.reduce(function(r,a,i){return r+(a*adjWeightings[i])},0)/100;
console.log(weightedResult1); // expected: 2.25 (pass)
console.log(weightedResult1.toPrecision(2)); // expected: "2.3" (pass)

weightedResult2 = scores2.reduce(function(r,a,i){return r+(a*adjWeightings[i])},0)/100;
console.log(weightedResult2); // expected: 2.35 (pass)
console.log(weightedResult2.toPrecision(2)); // expected: "2.4" (pass)

weightedResult3 = scores3.reduce(function(r,a,i){return r+(a*adjWeightings[i])},0)/100;
console.log(weightedResult3); // expected: 2.55 (pass)
console.log(weightedResult3.toPrecision(2)); // expected "2.6" (fail! whyyyyyyyyyy is this not "2.6"?????)
RobG
  • 142,382
  • 31
  • 172
  • 209
sasfrog
  • 2,410
  • 1
  • 17
  • 28
  • 1
    If in console you enter: `0.1 + 0.2` you should see `0.30000000000000004` It's a common floating point error https://stackoverflow.com/questions/588004/is-floating-point-math-broken – Roko C. Buljan Jul 16 '21 at 00:05
  • 1
    See [Is floating point math broken?](https://stackoverflow.com/questions/588004/is-floating-point-math-broken). – Unmitigated Jul 16 '21 at 00:06
  • 2
    Also this specific example is covered in the docs (toPrecision implements the same rounding as toFixed) [Using toFixed](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed#using_tofixed) – pilchard Jul 16 '21 at 00:07
  • @RokoC.Buljan Technically it's not an error, yes, yes, I know me been pedantic.. :) – Keith Jul 16 '21 at 00:13
  • 1
    @Keith technically no, but we humans perceive it as an error, yes. Long story short it's a mashup of us humans having ten fingers therefore adopting a silly base 10 and on the other hand computers operating in base 2. https://en.wikipedia.org/wiki/Floating-point_error_mitigation – Roko C. Buljan Jul 16 '21 at 00:16
  • 1
    @RokoC.Buljan Nice way of putting it. IOW: There are only 10 types of people who understand binary, those who do and those who don't. – Keith Jul 16 '21 at 00:18
  • Consider a function like `const myRound = x => Math.round(x * 10) / 10;` – RobG Jul 16 '21 at 00:25
  • A nice watch I might suggest with some amazing ideas in the way of adopting DEC64: https://youtu.be/DxnYQRuLX7Q?t=3391 (Douglas Crockford on code::dive 2017) And an implementation in C https://github.com/douglascrockford/DEC64 – Roko C. Buljan Jul 16 '21 at 00:27
  • @pilchard ah wow yes, indeed this exact example is indeed there! – sasfrog Jul 16 '21 at 00:48

1 Answers1

0

The floating point is there because of some limitations storing decimal values in binary. You tried to safeguard, but should do the rounding before acquiring the decimal value. You'll get correct results if you just do: console. log(Math.round(weightedResult3*10) / 10)

but should adjust the code for more efficiency.

zergski
  • 793
  • 3
  • 10