====== PHP RFC: Preserve Fractional Part in JSON encode ======
* Version: 1.1
* Date: 2014-12-25
* Author: Juan Basso, jrbasso@gmail.com
* Status: Implemented (PHP 7.0)
* First Published at: http://wiki.php.net/rfc/json_preserve_fractional_part
===== Introduction =====
This RFC suggests adding a new option to json_encode function to keep the fractional part on float values.
===== Proposal =====
JSON specs define that any type of number is treated as a number, not differentiating integer, floats, etc. It means the number 10.0 is considered as number and not as integer or float.
In PHP, using json_encode function the output of 10.0 is 10. It means decoding it convert a float/double value in integer. Making the encode and decode functions not symmetric (what is encoded is not exactly the same of decoded).
Testing the same behavior in other languages it shows that Go and JavaScript encode 10.0 to 10, but Python, C (lib jansson) and Ruby encode it to 10.0.
The goal of this RFC is propose a new json_encode option called JSON_PRESERVE_ZERO_FRACTION to allow the same behavior of other languages and allowing the symmetry of the encode and decode. See below what changes:
// Currently
echo json_encode(10.0); // Output 10
echo json_encode(10.1); // Output 10.1
var_dump(json_decode(json_encode(10.0))); // Output int(10)
var_dump(10.0 === json_decode(json_encode(10.0))); // Output bool(false)
// Proposed
echo json_encode(10.0); // Output 10
echo json_encode(10.1); // Output 10.1
echo json_encode(10.0, JSON_PRESERVE_ZERO_FRACTION); // Output 10.0
echo json_encode(10.1, JSON_PRESERVE_ZERO_FRACTION); // Output 10.1
var_dump(json_decode(json_encode(10.0, JSON_PRESERVE_ZERO_FRACTION))); // Output double(10)
var_dump(10.0 === json_decode(json_encode(10.0, JSON_PRESERVE_ZERO_FRACTION))); // Output bool(true)
A real case for this change is handling amounts, for example:
$order = ['subtotal' => 12.57, 'discount' => 12.57, 'total' => 0.0];
$encodeForSend = '{"subtotal":12.57,"discount":12.57,"total":0}';
// Send the JSON data
// After receive the JSON
$jsonReceived = '{"subtotal":12.57,"discount":12.57,"total":0}';
$order = json_decode($jsonReceived, true);
if ($order['total'] === 0.0) {
// Do something
}
On the code above, the if statement will fail because the number is not float as set when it was sent. The solution would be change it to be ''$order['total'] === 0.0 || $order['total'] === 0'' in case this value could assume null or boolean values for any reason.
Having this option will give the option for developers to use it and increase the interoperability with other languages and also to increase the symmetry in PHP functions for cases like storing data in JSON.
===== Backward Incompatible Changes =====
This RFC introduces no BC breaks.
===== Proposed PHP Version(s) =====
The next PHP 5.x.y.
===== RFC Impact =====
==== To SAPIs ====
No impact.
==== To Existing Extensions ====
No impact.
==== To Opcache ====
No impact.
==== New Constants ====
- JSON_PRESERVE_ZERO_FRACTION: Preserve the fraction part of float numbers with zero as decimal part.
==== Performance ====
The implementation of this RFC changed how to encode float numbers and it affected the performance of the json_encode even when not using the new flag.
The table below shows the performance of the code below (PS: not using the new flag):
$number = 1.0; // Or 1.1
$data = array_fill(0, 1000, $number);
$iterations = 10000;
while ($iterations-- > 0) {
json_encode($data);
}
+-------------------------------------------------------------------------------------+
| Number | With changes | Elapsed time | Peak Memory (real = false) |
+-------------------------------------------------------------------------------------+
| 1.0 | No | 2.6276361942291 | 331 688 |
| 1.0 | Yes | 1.9471561908722 (25.9% faster) | 331 496 |
| 1.1 | No | 2.672217130661 | 335 616 |
| 1.1 | Yes | 2.0137410163879 (24.6% faster) | 335 424 |
+-------------------------------------------------------------------------------------+
PS: Tests executed from Mac OS X 10.10.1 using CPU 2.3 GHz Intel Core i7 and 16GB of RAM.
PS 2: The changes made on this RFC doesn't affect the performance of other value types.
===== Future Scope =====
Define if this behavior should be enabled by default in PHP 7.
===== Proposed Voting Choices =====
This RFC requires a 50%+1 majority.
* Yes
* No
* Voting opened: 2015-01-11 02:55 UTC
* Voting closes: 2015-01-18 02:55 UTC
===== Patches and Tests =====
Currently implemented on https://github.com/php/php-src/pull/642
===== Implementation =====
Merged into jsond: https://github.com/bukka/php-src/pull/1
jsond merged into 7.0: https://github.com/php/php-src/pull/993
===== Rejected Features =====
None so far.