php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #50224 json_encode() does not always encode a float as a float
Submitted: 2009-11-19 05:45 UTC Modified: 2014-07-15 11:45 UTC
Votes:3
Avg. Score:4.7 ± 0.5
Reproduced:3 of 3 (100.0%)
Same Version:0 (0.0%)
Same OS:2 (66.7%)
From: christian dot lawrence at calorieking dot com Assigned:
Status: Closed Package: JSON related
PHP Version: 5.2SVN-2009-11-19 (snap) OS:
Private report: No CVE-ID:
 [2009-11-19 05:45 UTC] christian dot lawrence at calorieking dot com
Description:
------------
json_encode()-ing an integer when it is represented as floating point number results in a change of type when json_decode() decodes the output.

Examples of such floating point numbers are: -123.0, -1.0, 0.0, 1.0, 123.0

Reproduce code:
---------------
<?php

function jsonRoundTrip($f) {
        $e = json_encode($f);
        $d = json_decode($e);
        var_dump($f, $e, $d);
        echo "\n";
}

jsonRoundTrip(12.3);  // This is a float
jsonRoundTrip(12);  // This is an integer
jsonRoundTrip(12.0);  // This is an integer represented as a float
jsonRoundTrip(0.0);  // This is an integer represented as a float

?>


Expected result:
----------------
float(12.3)
string(4) "12.3"
float(12.3)

int(12)
string(2) "12"
int(12)

float(12)
string(4) "12.0"
float(12)

float(0)
string(3) "0.0"
float(0)


Actual result:
--------------
float(12.3)
string(4) "12.3"
float(12.3)

int(12)
string(2) "12"
int(12)

float(12)
string(2) "12"
int(12)

float(0)
string(1) "0"
int(0)


Patches

Add a Patch

Pull Requests

Pull requests:

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2009-11-19 08:51 UTC] jani@php.net
There's is just "number" type in JSON for numbers. And as such, this is working just like it should and PHP tries it's best at guessing what type the numbers might be.
 [2009-11-19 09:14 UTC] christian dot lawrence at calorieking dot com
Take a look at the format for "number" at http://www.json.org/ and observe the form for a "int frac".  This clearly indicates that there is support for representing a floating point number, even integer floats.

The json_encode() function clearly supports encoding of floating point numbers.  json_decode() is capable of deserialising "12.0" as a integer floating point number.

If you encode a floating point number, then one should expect to decode a floating point number.

If json_encode()/json_decode() is to be used as a serious and reliable data exchange format, then there should be no loss or conversion of primitive type information.

Please re-consider.
 [2009-11-19 15:12 UTC] jani@php.net
Yes, IF you were passing fractional part. But you're not. See the output of var_dump($f) in your results..
 [2009-11-20 10:39 UTC] christian dot lawrence at calorieking dot com
And there lines the problem - there is no way to express the fractional part if json_encode() does not even deal with it in the first place.

An integer and an integer represented as a floating point number are not the same thing because they have different types, as follows:
<?php
$a = 12;
var_dump($a);        //int(12)
$b = 12.0;           // This has a fractional part, hence it is a floating point number and not an integer
var_dump($b);        //float(12)
var_dump($a === $b); //bool(false)
?>

Numerically they have the same value, but we all know this to be true:
<?php
var_dump($a == $b); //bool(true)
?>

There is always a fractional part of any integer when it is represented as a floating point number.  It is implied and can, simply, be expressed by appending a ".0" to the integer part.  There is nothing in the JSON encoding rules on http://www.json.org/ which disallows this (see "int frac" form for specifics).

Decoding a valid and legitimate encoding of an integer when it is represented as a floating point number gives the correct PHP floating point type:
<?php
var_dump(json_decode("12.0")); //float(12)
?>

Decoding an integer-encoded stream also gives the correct PHP integer type:
<?php
var_dump(json_decode("12")); //int(12)
?>

I fail to see how my bug report is bogus when json_encode() is unable to produce a perfectly valid and legitimate encoding yet json_decode() is capable of doing the right thing.

Surely, the json_encode() implementation must be identifying the data type being encoded, presumably it is using equivalents for is_object() and is_array().  Why not use equivalents for is_int() or is_float() as well?

A reliable data interchange format should not purport to do any type-casting from the primitive types it was provided for encoding.  ie: If you encode a float then you should expect to decode a float.  As far as I am concerned json_encode() is un-reliable and fails to encode my float, which I have confirmed in my results.

I humbly ask that you reconsider your position.
 [2012-07-13 23:09 UTC] josh dot adell at gmail dot com
This is still an issue, specifically when JSON encoding for talking to APIs that 
don't allow mixed type arrays.

For instance, json_encode(array(1.2, 2.3)) properly encodes to "[1.2, 2.3]"

But, json_encode(array(1.0, 2.3)) encodes to "[1, 2.3]" which fails if the 
receiving end does not allow mixed-type arrays.

Any chance on this ever being fixed?

PHP Version: 5.3.10
 [2013-07-03 13:42 UTC] chr dot tatu at gmail dot com
The problem is still present in version 5.4.6.

var_dump says this values is float, but after applying json_encode and json_decode 
the value gets to be an int.

For the jsoncpp library there is a difference between int and float and that 
difference is acknowledged by the floating point.
 [2014-03-29 23:52 UTC] jrbasso at gmail dot com
The problem is still present in version 5.5.10.

I opened a PR to resolve it. https://github.com/php/php-src/pull/635
 [2014-07-15 11:44 UTC] tyrael@php.net
I think it would be nice looking into how others handle this issue.
Here is a comparision for the various json modules for python:
http://deron.meranda.us/python/comparing_json_modules/numbers
"Python floating point numbers (float) should be representable as JSON. JSON represents numbers with decimal fractions, and optional base-10 exponents. A floating-point number with a zero a fractional part, such as 1.0, could reasonably be converted to the JSON number 1 as well as 1.0; however no implementations choose to drop the fractional part."

the json ruby gem also seems to keep the fraction: 
#!/usr/bin/ruby
require 'json'
require 'pp'

pp JSON.parse(JSON.generate([1.0]))

outputs

[1.0]

I would be curious if there are other widely used json encoder implementations which are dropping the fraction for the integer numbers.

I think that keeping the fraction all times for floats wouldn't hurt anybody, but it could be useful for some people, and from my quick test this seems to be the common behavior, so I  think we should follow it too.
 [2014-07-15 11:45 UTC] tyrael@php.net
-Status: Not a bug +Status: Re-Opened
 [2014-07-16 02:48 UTC] jrbasso at gmail dot com
I tested the lib jansson (C) and it also keeps the fraction.
        json_t *array, *value;

        value = json_real(1.0);
        array = json_array();
        json_array_insert(array, 0, value);

        printf("%s\n", json_dumps(array, 0));

Outputs: [1.0]


I also tested go-lang and it gives integer value:

import "encoding/json"
fltB, _ := json.Marshal(1.0)
fmt.Println(string(fltB))

Output: 1


Javascript returns integer as well:

JSON.stringify(1.0)

Output: 1



Kevin Israel suggested on my pull request to create another flag to json_encode (see https://github.com/php/php-src/pull/642#issuecomment-48993303). What do you guys think? I can add it to the PR.
 [2015-01-19 18:06 UTC] stas@php.net
Automatic comment on behalf of jrbasso@gmail.com
Revision: http://git.php.net/?p=php-src.git;a=commit;h=ac7cfad3b54b04b7ff2d0e4bfd26e8b61d233613
Log: Fixed bug #50224 where float without decimals were converted to integer
 [2015-01-19 18:06 UTC] stas@php.net
-Status: Re-Opened +Status: Closed
 [2015-02-25 18:32 UTC] rn at alpha9marketing dot com
Can any of the involved persons please explain in their own words why it was deemed necessary to introduce yet another flag for this? What is so dangerous about appending a .0 to a float?
 
PHP Copyright © 2001-2017 The PHP Group
All rights reserved.
Last updated: Wed Apr 26 21:01:46 2017 UTC