0

I have a function which sum and print a totale of items in my web page,

the function actually just get the actual totale and sum the value of the new item to it.

The issue is that with a start value of 0 for total and them by adding 2.05 at 4th added item the total returns 6.21 instead of 8.20...

The console log of total and val while adding 0 2.05 / 2.05 2.05 / 4.1 2.05 / 6.16 2.05 / total 8.21

My functions looks like this:

function totale(val) {
  let totale = parseFloat($(".totale").text().replace("€", ""));
  $(".totale").text(formatEuro(totale + parseFloat(val)));
}

function formatEuro(val) {
  return (
    parseFloat(val)
      .toFixed(2)
      .replace(/\B(?=(\d{3})+(?!\d))/g, ".") + "€"
  );
}

How could i set and get the correct total?

The function totale is called inside another function addProducts() which is triggered on WebSocket message which take as parameter a JSON object which has the amount to be summed. The json passed is like:

products = {
  "type": "prod",
  "content": {
    "desc": "USB TO SERIAL",
    "prezzo": 2.20,
    "promo": {
      "desc": "PROMO IAN",
      "prezzo": 0.15
    }
  }
};

$('.addProduct').on('click', function() {
  addProdotto(products.content)
});

function addProdotto(prodotto) {
console.log(prodotto.prezzo)
  totale(
    prodotto.promo ? (prodotto.prezzo - prodotto.promo.prezzo) : prodotto.prezzo
  );
}

function totale(val) {
  let totale = parseFloat($(".totale").text().replace("€", ""));
  console.log(totale)
  $(".totale").text(formatEuro(parseFloat(totale) + parseFloat(val)));
}

function formatEuro(val) {
  return (
    parseFloat(val)
      .toFixed(2)
      .replace(/\B(?=(\d{3})+(?!\d))/g, ".") + "€"
  );
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button type="button" class="addProduct">ADD</button>

<h1 class="totale">€0.00</h1>

Here is an example with a button instead of websocket call

Here is what websocket sends when i receive the total 8.21 messages from all the calls:

{type: "prod", content: {desc: "USB TO SERIAL", prezzo: 2.2, promo: {desc: "PROMO IAN", prezzo: 0.15}}}

{"type": "prod", "content": {"desc": "USB TO SERIAL", "prezzo": 2.20, "promo": { "desc": "PROMO IAN", "prezzo": 0.15 }}}

{"type": "prod", "content": {"desc": "USB TO SERIAL", "prezzo": 2.20, "promo": { "desc": "PROMO IAN", "prezzo": 0.15 }}}

{"type": "prod", "content": {"desc": "USB TO SERIAL", "prezzo": 2.20, "promo": { "desc": "PROMO IAN", "prezzo": 0.15 }}}

EDIT 2:

Added the item twice via WebSocket the JSON is the same as above and here is the console log of TOTAL and VAL

enter image description here

After the first sum it become 2.052...

Kasper Juner
  • 832
  • 3
  • 15
  • 34
  • You are adding parseFloat(val) to the code of your function. Use a different varaible name and make sure it's `0` at the start – fourk Feb 11 '21 at 14:00
  • 3
    Please include a [mre] in your question. How are these functions being called? – Sebastian Simon Feb 11 '21 at 14:00
  • @SebastianSimon just added my code, but actually the issue doesn't happen on button click as in my real code the json comes from a websocket message – Kasper Juner Feb 11 '21 at 14:12
  • 2
    Voting to reopen this question. This is _not_ one of the many floating point dupes. `2.05 + 2.05 + 2.05 + 2.05 === 8.2` and `(2.05 + 2.05 + 2.05).toFixed(2) === "6.15"`. `8.21` can never occur in this calculation. The error is somewhere else. @KasperJuner Have you tried inspecting the WebSocket message? `(2.2 - 0.15) + (2.2 - 0.15) + (2.2 - 0.15) + (2.2 - 0.15)` results in `8.200000000000001`. Do you accidentally throw out all the `0`s in between? Just do `.toFixed(2)`. If you’re dealing with money, I’d recommend BigInts anyway: `(220n - 15n) * 4n === 820n`; `(Number(820n) / 100).toFixed(2)`. – Sebastian Simon Feb 11 '21 at 14:24
  • @SebastianSimon i've just added the messages from all 4 request from WebSocket, the issue is in the total as after second addition it become 2.502 instead of 2.50... It's even strange as i tryed to change the value passed to WebSocket to int instead of a float and that occurs anyway in the same way when i do "val / 100" – Kasper Juner Feb 11 '21 at 14:30
  • What exactly does the `console.warn` look like that produces `TOTALE 2.052` and where is it executed? I’m really struggling to reproduce this issue… – Sebastian Simon Feb 11 '21 at 14:40
  • @SebastianSimon been trying to reproduce it myself and I really can't see how that `2.050000000000000000003` can even come about. When I try to enter this, it just gets trunkated to `2.05`. The closest number to that I can get to even stick with the extra decimals is `2.0500000000000003` but summing two of these doesn't lead to `2.052` nor can I see how it can. – VLAZ Feb 11 '21 at 14:45
  • I just solved by creating a variable outside the function called "tot = 0.0" and then i'm summing the values to that variable and printing it's result. The issue i think was to parsing the 0.00 from the $('.totale') initially – Kasper Juner Feb 11 '21 at 14:47

1 Answers1

0

There are some numeric values that floating point numbers can not represent precisely. When you choose to use a floating point number, the computer will use the closest representation, which may have some small error. As you add multiple values, these errors accumulate and eventually will grow large enough to be noticeable.

In your specific case, you are dealing with currency. This is usually represented as whole numbers for dollars, Euros, etc. and hundreds for the fractional part, such as cents. The simplest way to avoid these rounding errors is to parse these numbers such that they are represented as cents. For example, treat 2.05 as 205 when calculating. Once your calculations are done, display the results with the decimal point before the last two digits.

peteru
  • 130
  • 1
  • 11