13

PHP seems to have a bug in the way it handles decimal precision in json_encode.

It's easy to see just by encoding a simple float:

echo json_encode(["testVal" => 0.830]);

// Prints out:
{"testVal":0.82999999999999996003197111349436454474925994873046875}

I'm not much of a server admin, so aside from going into the php.ini and changing serialize_precision to -1, is there anything I can do in my code to protect against this when I can't be sure it's running in an environment where that setting has been changed?

EDIT: I'm sure some comments will want to link against general discussions of why floating point imprecision exists. I know that. My question here is specifically about the best practice for dealing with it in PHP, and whether there is a way to code defensively against it. Surely there is a better way than sending floats as strings.

GoldenJoe
  • 7,874
  • 7
  • 53
  • 92
  • Does this happen in 7.0 as well? – Lee Armstrong May 09 '17 at 09:22
  • As the discussion on the bug report you linked to makes clear, this is not a bug but a deliberate feature. If your server is configured to serialize floating point numbers with high precision, `json_encode` will now respect this. – IMSoP May 09 '17 at 09:22
  • And if you write that value as string? Then you have exact that number you want. – René Höhle May 09 '17 at 09:24
  • `0.830` cannot be represented exactly as floating point. It is an approximation. PHP provides a better approximation (with a lot of decimal places). That's all. You can format the value as string using `number_format()` and put the string in JSON. Or, better, you can let it be a floating point number and do the formatting just before you want to put the value on screen. – axiac May 09 '17 at 09:30

1 Answers1

11

You should configure 'precision' and 'serialize_precision' params.

precision = 14
serialize_precision = -1

Test case:

php -r 'ini_set("precision", 14); ini_set("serialize_precision", -1); var_dump(json_encode(["testVal" => 0.830]));'
string(16) "{"testVal":0.83}"
Valery Viktorovsky
  • 6,487
  • 3
  • 39
  • 47
  • 1
    It might be helpful to link to the [documentation changelog](//php.net/manual/en/function.json-encode.php#refsect1-function.json-encode-changelog), where it is documented what changed. In particular, changing the `precision` no longer has an effect on `json_encode`. – Just a student May 09 '17 at 09:37
  • @Justastudent Thanks, thats true for 7.1, many guys still use 5.6 so answer above will be useful for everybody who use 5.6+. – Valery Viktorovsky May 09 '17 at 09:52
  • Yeah, I mentioned this in my original question. Is this the best solution, and if so, why? Are there any alternatives? – GoldenJoe May 09 '17 at 23:12
  • @GoldenJoe I think `serialize_precision = -1` is more convenient than using `number_format` across the code. It's possible to create `.user.ini` in site public dir and then add `serialize_precision = -1` option to it. – Valery Viktorovsky May 10 '17 at 11:04
  • Although not without its limits, a simple fix is to cast the value as a string. See my answer: https://stackoverflow.com/a/50944401/2338825 – texelate Jun 20 '18 at 09:10