1

When I write following code with reduce method, values are imprecise:

return bytes.reduce((accumulator, currentValue, index) => {
                 return accumulator + (currentValue * Math.pow(256,(bytes.length - 2) - index));
    }
)

eg. output numbers 5.99609375, 10.99609375, 7.99609375, 14.99609375 But when I write following code result is precise:

let result = 0.0;
for (let i = 0; i < bytes.length - 1; ++i) {
                result = result + (bytes[i] * Math.pow(256, (bytes.length - 2) - i));
    }
return result

eg. output numbers 5, 10, 7, 14 Input byte arrays are:

Uint8Array(4) [0, 0, 5, 255]
Uint8Array(4) [0, 0, 10, 255]
Uint8Array(4) [0, 0, 7, 255]
Uint8Array(4) [0, 0, 14, 255]

Why is that? Is there a way to make reduce method work precisely?

const res = document.getElementById('result');
const res2 = document.getElementById('result2');
const arr = a = [
  [0, 0, 5, 255],
  [0, 0, 10, 255],
  [0, 0, 7, 255],
  [0, 0, 14, 255]
];
const fn = (bytes) => {
  let result = 0.0;
  for (let i = 0; i < bytes.length - 1; ++i) {
    result = result + (bytes[i] * Math.pow(256, (bytes.length - 2) - i));
  }
  return result
}
fn2 = (bytes) => {
  return bytes.reduce((accumulator, currentValue, index) => {
    return accumulator + (currentValue * Math.pow(256, (bytes.length - 2) - index));
  })
}
res.innerText += `${arr.map(fn)}`;
res2.innerText += `${arr.map(fn2)}`;
<div id="result"></div>
<div id="result2"></div>
Zydnar
  • 1,472
  • 18
  • 27
  • 3
    both code snippets produce a single number and not several. Also, give a sample input to reproduce said behavior. – ASDFGerte May 03 '18 at 13:09
  • Riht, but these are eg. output numbers, not eg. output Arrays. – Zydnar May 03 '18 at 13:10
  • Also, please provide the bytes array. – Mihai Alexandru-Ionut May 03 '18 at 13:10
  • 2
    You have not provided the initial value of the accumulator in the array#reduce. – Hassan Imam May 03 '18 at 13:11
  • 1
    I do not see the initial `0` accumulator in the call of Array#reduce(fx, accumulator) – philipp May 03 '18 at 13:13
  • The default is the first array element, i actually thought it was plain summation but that comment is the answer very likely. – ASDFGerte May 03 '18 at 13:14
  • Please provide real [mcve]s, preferably as [a live demo](https://stackoverflow.blog/2014/09/16/introducing-runnable-javascript-css-and-html-code-snippets/). – Quentin May 03 '18 at 13:21
  • 1
    @MihaiAlexandru-Ionut Everything on stackoverflow must be upvoted - for properly formatted and solvable questions like this i don't get mad, unlike other questions for which i could write a bot to post within a day. – ASDFGerte May 03 '18 at 13:23
  • The two iteration methods are not equivalent, since the `for` loop misses the final element. If you change that, you will get the same result in both cases. – VLAZ May 03 '18 at 13:35
  • @vlaz You are right, but the question is why, and how to do same in reduce. – Zydnar May 03 '18 at 13:42
  • You cannot easily do it. This is because `reduce` is designed to run through the whole array - you could basically check if you've reached the last element and then exclude it but it might just be easier to just remove the last element before running `reduce` on the array. Something like `arr.slice(0, arr.length - 1).reduce( /* ... */ )` can do it. – VLAZ May 03 '18 at 13:53
  • @vlaz in my case `bytes.slice(0.3).reduce...` was the answer. I see now also, it was wrong question, but it solved something, I spend too much time thinking, so thank you. – Zydnar May 03 '18 at 13:57

2 Answers2

1

The for condition is wrong Should be

 for (let i = 0; i < bytes.length ; ++i) {
Mihai Alexandru-Ionut
  • 47,092
  • 13
  • 101
  • 128
Tiago Coelho
  • 5,023
  • 10
  • 17
1

Why reduce is affected by floating point issue and for loop not? When I write following code with reduce method, values are imprecise

That is not a case of floating point issue. If you look at the results you will see something like this:

5.99609375

When you have floating point precision is something like this

0.020000000000000004

The problem is in the for loop because you're iterating only the first n-1 items from bytes array.

for (let i = 0; i < bytes.length - 1; ++i) {

Just iterate all the array items.

for (let i = 0; i < bytes.length; ++i) {

You are seeing two values because reduce method run through all the array items, and for loop run through first n-1 items.

Unfortunately, there is no way, of course, to get the built-in version of reduce to exit prematurely.

But you can use slice method.

return bytes.slice(0, -1).reduce((accumulator, currentValue, index) => {
    return accumulator + (currentValue * Math.pow(256,(bytes.length - 2) - index));
})
Mihai Alexandru-Ionut
  • 47,092
  • 13
  • 101
  • 128
  • Good answer but in for loop NOT in reduce I get expected output, so problem is not in for loop. – Zydnar May 03 '18 at 13:36
  • 2
    @Zydnar, you are getting a floating number because it's reached a step when you have `Math.pow(256, -1)` which is `1/256`. But in reduce method you cannot use `break` method or something like this: https://stackoverflow.com/questions/36144406/how-to-break-on-reduce-method – Mihai Alexandru-Ionut May 03 '18 at 13:41
  • You are seeing two values because `reduce` method run through all the array items, and `for` loop run through first `n-1` items. – Mihai Alexandru-Ionut May 03 '18 at 13:41
  • Ok, I see it now. – Zydnar May 03 '18 at 13:45