6

Hmm I have an issue with roundings on the client side which is then validated in the backend and the validation is failing due to this issue. Here is the previous question Javascript and C# rounding hell

So what I am doing is:

On client side:

I have 2 numbers: 50 and 2.3659
I multiply them: 50 * 2.3659  //118.29499999999999
Round to 2 decimal places: kendo.toString(50 * 2.3659, 'n2') //118.29

In backend(C#):

I am doing the same: 50 and 2.3659
I multiply them: 50 * 2.3659  //118.2950
Round to 2 decimal places: Math.Round(50 * 2.3659, 2) //118.30

And validation is failing. Can I do something on the client side?

Giorgi Nakeuri
  • 35,155
  • 8
  • 47
  • 75
  • 2
    You are aware that [floating math "is broken"](https://stackoverflow.com/questions/588004/is-floating-point-math-broken), right? – deceze Jun 23 '17 at 08:55
  • Right, but the question is what can I do in this case? Do you suggest me to throw away business validations and commit incorrect results? – Giorgi Nakeuri Jun 23 '17 at 08:57
  • Multiply them as integers, not as floats. `50 * 23659` - then move the decimal point as you see fit. Then you'll get same results everywhere. It's not ideal, but it will work. – Mjh Jun 23 '17 at 08:57
  • Can rounding 2.3659 before multiplication be a solution? – Mistalis Jun 23 '17 at 08:57
  • 1
    @Mistalis, no way. – Giorgi Nakeuri Jun 23 '17 at 08:57
  • Maybe send two numbers, result+Number.Epsilon and result-Number.Epsilon, and check for a<=serverside<=b – Jonas Wilms Jun 23 '17 at 09:01
  • 2
    Then your best bet is probably a library which defines an actual decimal type or allows you to do mathematical operations on numbers as strings. – deceze Jun 23 '17 at 09:01
  • can you suggest the library. Just checked math.js and it is more weird: `math.multiply(50, 2.3659) == 118.295`, `math.round(math.multiply(50, 2.3659), 2) == 118.29`, `math.round(118.295, 2) == 118.3` – Giorgi Nakeuri Jun 23 '17 at 09:15
  • You could try bignumber.js (MikeMcl) I guess you can just google the github link. It should give you the correct results. But there are other more-than-capable libraries out there. – Fygo Jun 23 '17 at 09:31

3 Answers3

0

Haven't tested this extensively, but the function below should emulate the 'MidPointToEven' rounding:

function roundMidPointToEven(d, f){  
    f = Math.pow(10, f || 0);  // f = decimals, use 0 as default
    let val = d * f, r = Math.round(val); 
    if(r & 1 == 1 && Math.sign(r) * (Math.round(val * 10) % 10) === 5)
     r +=  val > r ? 1 : -1;  //only if the rounded value is odd and the next rounded decimal would be 5: alter the outcome to the nearest even number
    return r / f;
}

for(let d of [50 * 2.3659, 2.155,2.145, -2.155, 2.144444, 2.1, 2.5])
    console.log(d, ' -> ', roundMidPointToEven(d, 2)); //test values correspond with outcome of rounding decimals in C#
Me.Name
  • 12,259
  • 3
  • 31
  • 48
0

Can you try parseFloat and toFixed functions as follows :

   var mulVal = parseFloat(50) * parseFloat(2.3659);
   var ans = mulVal.toFixed(2);
   console.log(ans);
Gunjan Aswani
  • 118
  • 1
  • 11
0

Javascript Arithmetic is not always accurate, and such erroneous answers are not unusual. I would suggest that you use Math.Round() or var.toFixed(1) for this scenario.

Using Math.Round:

var value = parseFloat(50) * parseFloat(2.3659);
var rounded = Math.round(value);
console.log(rounded);

Prints 118 to the console.

Using toFixed() method:

var value = parseFloat(50) * parseFloat(2.3659);
var rounded = value.toFixed(1);
console.log(rounded);

Prints 118.3 to the console.

Note that using toFixed(2) will give the value as 118.29.

Hope this helps!

lloydaf
  • 605
  • 3
  • 17