Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sending a numeric value in http request with swift, adds 000000000000001 to the actual value

I'm trying to perform a HTTP request to a server: the content is a JSON object, which contains a numeric value for the key "amount". If the "amount" is a value with a decimal digit, e.g. 1.6, the request will contain the value 1.6000000000000001, and this value is not accepted by the Server (the api is Java made and the type is a float . I cannot send a String to the server, since the API that receives the data from me can only accept numbers for the "amount". I tried to perform the request with Siesta Framework or with dataTask, but the result is always the same

this is how I create the request (I omitted the less important parts)

let jsonData = try! JSONSerialization.data(withJSONObject: jsonObject) // jsonObject contains the Double value "amount"
let request = URLRequest(url: url)
request.httpBody = jsonData
let task = URLSession.shared.dataTask(with: request)    
task.resume()
like image 227
AleGiovane Avatar asked Mar 08 '23 03:03

AleGiovane


1 Answers

Without code that fully reproduces the issue, it’s hard to say for sure, but I imagine what you’re seeing is this behavior:

let amount = 1.6  // Double
let jsonObject = ["amount": amount]
let jsonData = try! JSONSerialization.data(withJSONObject: jsonObject)
String(data: jsonData, encoding: String.Encoding.utf8)

Swift Foundation’s JSON serialization always formats numeric values to their full precision — so the double-precision number 1.6 gets formatted as 1.6000000000000001.

Solution 1: Send a string

You can and should send a string if the server accepts it:

let amount = "1.6"  // String
let jsonObject = ["amount": amount]
let jsonData = try! JSONSerialization.data(withJSONObject: jsonObject)
String(data: jsonData, encoding: String.Encoding.utf8)

Note that a string is the only correct way to send this value if you are dealing with money or anything else where exact values matter: even if you spell it as 1.6, a standard JSON parser will likely convert it to a floating point on the receiving end.

Solution 2: Use Decimal to alter the formatting

If you just need to format it with less precision to make it pass validation on the server for some reason, you can embed it in the JSON as a Decimal instead of a Double and it will get formatted differently:

let amount = 1.6
let jsonObject = ["amount": Decimal(amount)]
let jsonData = try! JSONSerialization.data(withJSONObject: jsonObject)
String(data: jsonData, encoding: String.Encoding.utf8)
// {"amount":1.6}

You can even manipulate the Decimal to round to a certain precision.

Note, however, that this does not spare you from floating point precision issues: you are still sending a float according to the JSON spec, and it will still most likely be parsed as a float on the receiving end.

like image 52
Paul Cantrell Avatar answered May 07 '23 06:05

Paul Cantrell