-1

I am working with web api 2 which I coincidentally realized that it rounds the long type value in the response model if it is longer than 16 digits.

(simplifying the example for the sake of the question) Assume I have a web api method as "GetOrders" which returns list of orderVm model which is defined as below:

public class OrderVm{
    public int OrderID {get;set;}
    public long? OrderNumber {get;set;}
}

What is happening is, if my order number value is

  • 1234567890123459 there is no issue - total of 16 digits.
  • 1234567890123499 there is no issue - total of 16 digits.
  • 12345678901234599 this is the problematic one. total of 17 digits. It is rounding this to 12345678901234600

Below is my simplified example API Method:

        [HttpPost]
        [Route("get-orders/")]
        public IHttpActionResult GetOrders(PostedVm postedVm)
        {
              var orderDtoList = _orderManager.GetOrders(postedVm.Active); // I checked the value in orderDtoList and it is not rounded at this moment...
              var orderVmList  = MapDtoToVm(orderDtoList);    
              return Ok(orderVmList);// I thought maybe something happens during the property mapping (which is just using simple reflection) but I checked the value in orderVmList and it is not rounded at this moment...
        }

Even though right before returning the response, I checked that the values actually were not rounded.. But somehow on the client, it is coming as rounded. I thought maybe the browser does some magic (for some reason) but I checked my payload on postman and fiddler, it was showing that the value as rounded in the response payload.

This sounds something related to web api configuration but if so, how I can set it to not modify the data? And I am also curious about "why it is happening after 16 digits?"

Just in case I added my WebApiConfig class below:

 public static class WebApiConfig {
    public static void Register(HttpConfiguration config) {
        //GZip Compression
        GlobalConfiguration.Configuration.MessageHandlers.Insert(0, new ServerCompressionHandler(new GZipCompressor(), new DeflateCompressor()));

        // Web API routes
        config.MapHttpAttributeRoutes();
        config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));

        config.Routes.MapHttpRoute(
        name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new {
            id = RouteParameter.Optional
        });
    }
}
curiousBoy
  • 6,334
  • 5
  • 48
  • 56
  • The number is too large for JavaScript to handle as an integer. Since you're unlikely to do any arithmetic with it, just handle it as a string. – Guy Incognito May 30 '20 at 20:58
  • there is no arithmetic process on those values, simply used as a static value on UI. Yes could be converted to string, but WHY server rounds it. You think it is because of the JSON? As far as I read JSON is an abstract format that is not exclusively targeted at JavaScript, the actual target environment determines the boundaries of what can be interpreted – curiousBoy May 30 '20 at 21:06
  • 3
    It is possible that you're looking at a formatted payload ("prettified JSON") instead of the raw payload, which would cause the number to go through a JSON parser and become inaccurate. – Guy Incognito May 30 '20 at 21:10
  • @GuyIncognito - you are right.. Even in fiddler, I checked the raw response, and it was not rounded. But beautified version was showing rounded. Wow I wouldnt expected that.. Thanks a lot! But I was thinking that beautyfied version was only for human! I would expect browser to use the raw version of it...You want to share this as answer? or I can share and reference you? – curiousBoy May 30 '20 at 21:23
  • 2
    Json.Net doesn't seem to have a problem with this at all. Can you elaborate on what context you're seeing the problem in? – Lasse V. Karlsen May 30 '20 at 21:24
  • 3
    The browser *does* use the raw version, but again, as soon as you parse the JSON the number is too large for JavaScript. – Guy Incognito May 30 '20 at 21:24
  • @GuyIncognito I see. But if you look at the rounded value, it is even bigger than raw data. I mean it rounds it to 1 above... So how come it is being because of the "too large" for javascript? Just curious.. 12345678901234599 is less than 12345678901234600 no? (lol cant believe asking this) – curiousBoy May 30 '20 at 21:34
  • 2
    Floating point numbers don't have infinite precision. See https://stackoverflow.com/a/1379973 – Guy Incognito May 30 '20 at 21:37
  • @GuyIncognito - now I remember. Thanks a lot. I think that was one of the "10 reason you should never use javascript for arithmetic operations" – curiousBoy May 30 '20 at 21:41
  • 2
    @curiousBoy post may be downvoted because of regular "does not show any research" - post clearly blaming C#/web API for something that is not even true which indeed you later verified to be the case... Maybe clearly specifying that problem is in browser getting data from API in the title would have helped (by setting initial expectation) – Alexei Levenkov May 30 '20 at 22:13
  • 1
    Note that your edit still blames C#/web API and clearly makes false statement - "In the raw response my values were not rounded."... – Alexei Levenkov May 30 '20 at 22:15
  • @AlexeiLevenkov - at the time of the question asked, I had no idea the raw response was not rounded. Regarding the research, I did research. The problem was I focused on the wrong spot so I couldnt find answer. Then I posted the question like that. Once I have the answers and edit the question, the comments and answers does not make sense. I was keeping the title as it is, if someone have the similar issue and search like that.. Thanks for the suggestion though – curiousBoy May 30 '20 at 22:25

2 Answers2

1

Thanks to Guy Incognito that he pointed me to the raw response of the JSON. In the raw response my values were not rounded. But when I parse the JSON the number was being too large for JavaScript and as floating point numbers don't have infinite precision, I was having this issue. I ended up changing my data type to string as I don't do any arithmetic operations with that anyways.

Thanks to Guy Incognito again, this SO post was also very helpful.

curiousBoy
  • 6,334
  • 5
  • 48
  • 56
0

This is most likey due to number represented as IEEE 754 (double in C#).

Due to the binary representation rounding may apply, so that 12345678901234599 and 12345678901234600 have the same binary representation.

You can observe this behavior in C# as well:

double x = 12345678901234599;
double y = 12345678901234600;
Console.WriteLine(x == y); // Prints true

https://dotnetfiddle.net/G0KOqZ

EDIT: Despite my original answer the JSON standard does not specify numeric limits, but the problem is the same.

This specification allows implementations to set limits on the range and precision of numbers accepted. Since software that implements IEEE 754 binary64 (double precision) numbers [IEEE754] is generally available and widely used, good interoperability can be achieved by implementations that expect no more precision or range than these provide, in the sense that implementations will approximate JSON numbers within the expected precision.

https://www.rfc-editor.org/rfc/rfc8259#section-6

Community
  • 1
  • 1
Christian Held
  • 2,321
  • 1
  • 15
  • 26
  • Newtonsoft JSON https://dotnetfiddle.net/aQMjhi and System.Text.Json https://dotnetfiddle.net/Ayq1jC handle int64 perfectly fine – Martheen May 30 '20 at 22:30