8

I am implementing an Hybrid mobile application in which I have to represent our Desktop application written in C#.

When rounding off a value, the value differs between Desktop and Mobile application.

Example

Code used in C#:

Math.Round (7060.625, 2); // prints 7060.62
Math.Round (7060.624, 2); // prints 7060.62
Math.Round (7060.626, 2); // prints 7060.63

Code used in JS:

+(7060.625).toFixed(2); // prints 7060.63 (value differs)
+(7060.624).toFixed(2); // prints 7060.62
+(7060.626).toFixed(2); // prints 7060.63

How can I change the JS code to represent the value as in C#.

Note:

We can make C# to represent the value as in JS, using Math.Round (7060.625, 2, MidpointRounding.AwayFromZero);

But I have no option to change in there.

EDIT 1

Rounding off decimals is not fixed. It is chosen by user in mobile application.

Naveen Kumar V
  • 2,559
  • 2
  • 29
  • 43
  • 2
    I don't think the linked question is the same. Sure, its about rounding, but this question isn't just about general rounding. Its trying to get it to round down a decimal that ends in 5 but still round up for 6-9. (duplicate has been removed) – Blake Thingstad Sep 28 '17 at 13:49
  • To my knowledge Javascript has no such built-in function – nl-x Sep 28 '17 at 13:50
  • @BlakeThingstad - the **2nd** answer does touch on that (search on text "toFixed() also will NOT round correctly in some cases") – Igor Sep 28 '17 at 13:53
  • https://stackoverflow.com/a/35827227/4735052 maybe this will help. – p__d Sep 28 '17 at 13:53
  • 1
    Difference is that in case of 5 JS rounds UP and C# down. Isn't the round up in case of 5 desired. Math suggests so. https://math.stackexchange.com/questions/218299/how-to-round-0-45-is-it-0-or-1 – DanteTheSmith Sep 28 '17 at 13:54
  • 2
    @DanteTheSmith it's not that simple as up vs down. C# by default rounds toward the even, so 1.5 rounds to 2 and 2.5 also rounds to 2. C# has two rounding modes.. toward even and away from zero. Java by contrast has seven or eight, depending on what you count as an actual rounding mode. https://docs.oracle.com/javase/7/docs/api/java/math/RoundingMode.html – Samuel Neff Sep 28 '17 at 13:56
  • 1
    @Igor not the same issue.. that question you referenced as a duplicate is about dealing with floating point issues and the question here is about an alternate rounding algorithm that JS doesn't support natively. – Samuel Neff Sep 28 '17 at 13:57
  • 1
    @SamuelNeff oh god I didn't realize rounding had so many confusing options! Rounding towards even, towards 0, positive infinity, down, up, etc, too much for me! – Blake Thingstad Sep 28 '17 at 13:58
  • @SamuelNeff - fair enough. Wouldn't the question you reference in your answer serve as a good alternate duplicate though? – Igor Sep 28 '17 at 14:01
  • @Igor In the referenced question, the person asking the question knew exactly what they were asking for, knew the term, and got an answer. In this question the OP wants something equivalent to C#. Just marking it as a duplicate would not necessarily be clear to them or anyone else that comes across it that the "duplicate" really is a duplicate. The one sentence I added clearly stating this makes it worthwhile. – Samuel Neff Sep 28 '17 at 14:10

2 Answers2

16

You need a custom implementation of rounding to implement "banker's rounding" or to-even rounding.

From:

Gaussian/banker's rounding in JavaScript

function evenRound(num, decimalPlaces) {
    var d = decimalPlaces || 0;
    var m = Math.pow(10, d);
    var n = +(d ? num * m : num).toFixed(8); // Avoid rounding errors
    var i = Math.floor(n), f = n - i;
    var e = 1e-8; // Allow for rounding errors in f
    var r = (f > 0.5 - e && f < 0.5 + e) ?
                ((i % 2 == 0) ? i : i + 1) : Math.round(n);
    return d ? r / m : r;
}

console.log( evenRound(1.5) ); // 2
console.log( evenRound(2.5) ); // 2
console.log( evenRound(1.535, 2) ); // 1.54
console.log( evenRound(1.525, 2) ); // 1.52
Samuel Neff
  • 73,278
  • 17
  • 138
  • 182
2

If you control both client side and server side you can follow this super simple pattern, it works fine for edge cases and regular case:

Let's look at 2.155, 2.145 (Midpoint issue) and 2.166, 2.146 (Regular).

C#:

public static decimal RoundFromJavaScript(this Decimal value)
{
    return Decimal.Round(value, 2, MidpointRounding.AwayFromZero);
}

//-Midpoint Issue 
RoundFromJavaScript(2.155); --> 2.16             
RoundFromJavaScript(2.145); --> 2.15
//-Regular 
RoundFromJavaScript(2.166); --> 2.17             
RoundFromJavaScript(2.146); --> 2.15

*I've omitted the m decimal sign, it should be 2.155m

JavaScript:

function roundToTwo(num) {
    return +(Math.round(num + "e+2") + "e-2");
}

//-Midpoint Issue
roundToTwo(2.155) --> 2.16           
roundToTwo(2.145) --> 2.15

//-Regular
roundToTwo(2.166) --> 2.17            
roundToTwo(2.146) --> 2.15
Shahar Shokrani
  • 7,598
  • 9
  • 48
  • 91
  • 1
    Will give it a try sometimes later and let you know of the result. Appreciate your effort. :) – Naveen Kumar V Mar 18 '20 at 04:13
  • 1
    @NaveenKumarV I'm surprised that this issue does not invite a lot more traffic... – Shahar Shokrani Mar 18 '20 at 09:17
  • This doesn't work. `Console.WriteLine(Math.Round(53.445, 2));` => 53.44, where as `roundToTwo(53.445)` -> 53.45 – Langdon Mar 30 '21 at 19:04
  • Hey @Langdon, not sure why the down-vote? the answer is about `Decimal.Round(53.445, 2, MidpointRounding.AwayFromZero)` -> 53.45 not `Console.WriteLine(Math.Round(53.445, 2));` -> 53.44 – Shahar Shokrani Mar 30 '21 at 21:27
  • Your answer doesn't answer the original question which was how to round like C# does by default (`ToEven`). The OP suggested Javascript's `toFixed` already behaves like C#'s `AwayFromZero`. I can't change my vote unless the answer is edited anyway. – Langdon Apr 01 '21 at 00:33
  • The OP only use the `Math.Round(...)` as an example, and I suggested him a different working solution (his question was not about the `Math.Round`). – Shahar Shokrani Apr 01 '21 at 08:18