rfc:precise_float_value

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
rfc:precise_float_value [2015/08/05 06:19]
yohgaki
rfc:precise_float_value [2017/09/22 13:28] (current)
Line 1: Line 1:
 ====== PHP RFC: More precise float value handling ====== ====== PHP RFC: More precise float value handling ======
-  * Version: 0.9+  * Version: 1.1
   * Date: 2015-07-30   * Date: 2015-07-30
-  * Author: Yasuo Ohgaki <yohgaki@ohgaki.net> +  * Author: Yasuo Ohgaki <yohgaki@ohgaki.net>, Jakub Zelenka <bukka@php.net> 
-  * Status: Under Discussion+  * Status: Implemented (PHP 7.1)
   * First Published at: http://wiki.php.net/rfc/precise_float_value   * First Published at: http://wiki.php.net/rfc/precise_float_value
  
 ===== Introduction ===== ===== Introduction =====
  
-This RFC is based on float like JSON numeric discussion and proposes more precise float value handling overall.+This RFC is based on the discussion about displaying float values in json_encode and proposes more precise float value handling overall.
  
-JSON is used to exchange data between systems. Although [[https://tools.ietf.org/html/rfc7159|JSON RFC "6 Numbers"]]  does not require specific implementation for float/int type, float value should be able to handle precise float values as much as possible by default. +JSON is used to exchange data between systems. Although [[https://tools.ietf.org/html/rfc7159|JSON RFC "6 Numbers"]] does not require specific implementation for float/int type, float value should be handled as precise as possible by default.
  
-Currentlyjson_encode() uses PG(precision) which is 14. IEEE 754 double supports more precision and serialize()/var_export() uses PG(serialize_precision)=17 to be more precise. Since json_encode() uses PG(precision), json_encode() removes lower digits of fraction parts and destroys original value even if PHP's float could hold more precise float value. +Currently json_encode() uses EG(precision) which is set to 14. That means that 14 digits at most are used for displaying (printing) the number. IEEE 754 double supports higher precision and serialize()/var_export() uses PG(serialize_precision) which set to 17 be default to be more precise. Since json_encode() uses EG(precision), json_encode() removes lower digits of fraction parts and destroys original value even if PHP's float could hold more precise float value.
  
 <code php> <code php>
Line 40: Line 40:
 </code> </code>
  
-PHP's float type stores "raw" IEEE 754 double and could store accurate fraction value up 17 digits.+PHP's float type stores "raw" IEEE 754 double and could display accurate fraction value up to 17 digits.
  
-Current PHP outputs meaningless values for oversized PG(precision)/PG(serialize_precision).+Current PHP outputs meaningless values for oversized EG(precision)/PG(serialize_precision).
  
 <code php> <code php>
Line 57: Line 57:
 float(0.12345678901234567736988623209981597028672695159912109375) float(0.12345678901234567736988623209981597028672695159912109375)
 </code> </code>
 +
 +That is caused by used mode for double to string conversion.
  
 ===== Proposal ===== ===== Proposal =====
  
-  * Introduce PG(precision)=-1 and PG(serialize_precision)=-1 that uses zend_dtoa()'s 0 mode that rounds to nearest value. (-1 is used to indicate 0 mode)+This RFC proposes to introduce a new setting EG(precision)=-1 and PG(serialize_precision)=-1 that uses zend_dtoa()'mode which uses better algorigthm for rounding float numbers (-1 is used to indicate 0 mode).
  
-Since JSON is used extensively for web apps, we may have JSON specific INI setting for better compatibility and ease of use.+The RFC also proposes changing ini for JSON precision to PG(serialize_precision).
  
-  * Add JSON_G(json_precision) - Add JSON module specific precision INI setting.+Followings are sample codes and outputs of the proposed patch. 
 + 
 +NEW behavior 
 +<code php> 
 +<?php 
 +$v = 10.0000000000001; 
 + 
 +ini_set('precision', -1)
 +ini_set('serialize_precision', -1); 
 + 
 +var_dump($v); 
 +echo var_export($v, true), PHP_EOL; 
 +echo json_encode($v), PHP_EOL; 
 +echo $v, PHP_EOL; 
 +?> 
 +</code> 
 + 
 +  float(10.0000000000001) 
 +  10.0000000000001 
 +  10.0000000000001 
 +  10.0000000000001 
 + 
 + 
 + 
 +OLD behavior 
 +<code php> 
 +<?php 
 +$v = 10.00000000000001; 
 + 
 +ini_set('precision', 14); 
 +ini_set('serialize_precision', 17); 
 + 
 +var_dump($v); 
 +echo var_export($v, true), PHP_EOL; 
 +ini_set('serialize_precision', 14); 
 +echo json_encode($v), PHP_EOL; 
 +ini_set('serialize_precision', 17); 
 +echo $v, PHP_EOL; 
 +?> 
 +</code> 
 + 
 +  float(10) 
 +  10.000000000000011 
 +  10 
 +  10 
 + 
 + 
 +Please note that IEEE float cannot store exactly precise values. e.g. Result of "10/3" - see phpt of the patch. Even with this proposal, there will be rounding errors, but the behavior becomes similar to other languages and values are more precise in many cases.
  
  
 ===== Backward Incompatible Changes ===== ===== Backward Incompatible Changes =====
  
-None because old INI default is kept.+Setting mode 0 as default can mean that the rounding will be more precise which also means that the rounding might be different in var_export()/serialize(). 
 + 
 +The BC break could happen only if someone would rely on exact output but that shouldn't be the case. All our existing tests passes when 0 mode is used. 
 + 
 +None when old INI value is used.
  
 ===== Proposed PHP Version(s) ===== ===== Proposed PHP Version(s) =====
  
-PHP 7.0.0 - 0 mode and JSON_G(json_precision) +  * PHP 7.1
-PHP 5.6.0 - 0 mode. JSON uses PG(serialize_precision) for float precision.+
  
 ===== RFC Impact ===== ===== RFC Impact =====
Line 82: Line 134:
 ==== To Existing Extensions ==== ==== To Existing Extensions ====
 PHP overall PHP overall
- * 0 mode float outputs values rounded to nearest. +  * 0 mode (EG(precision)= -1) float outputs values rounded to nearest.
- +
-Standard module +
- * serialize(), var_export() +
- +
-JSON +
- * json_encode() - PHP7.0: Use 0 mode. PHP5.6: Use PG(serialize_precision) +
- +
-WDDX? +
- +
-XML_RPC?+
  
 +Standard module and JSON
 +  * serialize(), var_export(), json_encode - Use PG(serialize_precision) and 0 mode by default.
  
 ==== To Opcache ==== ==== To Opcache ====
Line 110: Line 154:
   * php.ini-production values : 14 Unmodified   * php.ini-production values : 14 Unmodified
  
-serialize_precision(PHP5.6) +serialize_precision
-  * hardcoded default values : 17 Unmodified +
-  * php.ini-development values : 17 Unmodified +
-  * php.ini-production values : 17 Unmodified +
- +
-serialize_precision(PHP7) +
-  * hardcoded default values : -1 +
-  * php.ini-development values : -1 +
-  * php.ini-production values : -1 +
- +
-json_precision(NEW - PHP7 only)+
   * hardcoded default values : -1   * hardcoded default values : -1
   * php.ini-development values : -1   * php.ini-development values : -1
Line 133: Line 167:
  
 PHP uses "raw" IEEE 754 value internally regardless of precision settings. Therefore, this RFC does not affect internal computation. PHP uses "raw" IEEE 754 value internally regardless of precision settings. Therefore, this RFC does not affect internal computation.
 +
  
 ===== Future Scope ===== ===== Future Scope =====
  
-  Remove PG(serialize_precisionsetting. If we are going to remove PG(serialize_precision) in the future, we may be better to have JSON_G(json_precisionas one may need specific precision for JSON data.+WDDX 
 +  wddx_serialize_vars/value() - Use PG(serialize_precision) and 0 mode. It uses EG(precisioncurrently.
  
-===== Proposed Voting Choices =====+XML_RPC 
 +  * xmlrpc_encode() - Use PG(serialize_precision) and 0 mode. It uses EG(precision) currently.
  
-Requires a 50%+1 majority (see [[voting]]) 
  
-<doodle title="Introduce 0 mode to PG(precision)/PG(serialize_precision) for PHP 7.0" auth="yohgaki" voteType="single" closed="True"> +===== Voting =====
-   * Yes +
-   * No +
-</doodle>+
  
-.+Requires a 2/3 majority for the first vote (mode 0 to become default for serialize precision) and 50%+1 majority for the second vote (changing default for json).
  
-<doodle title="Add JSON_G(json_precisionfor JSON module only setting for PHP 7.0" auth="yohgaki" voteType="single" closed="True">+The both votes are straight Yes/No votes. 
 + 
 +<doodle title="Should mode 0 be introduced and set as default for PG(serialize_precision)" auth="bukka" voteType="single" closed="true">
    * Yes    * Yes
    * No    * No
 </doodle> </doodle>
  
-+<doodle title="Should PG(serialize_precisionbe used instead of EG(precisionin json_encode" auth="bukka" voteType="single" closed="true">
- +
-<doodle title="PG(precision)/PG(serialize_precision0 mode support for PHP 5.6 " auth="yohgaki" voteType="single" closed="True">+
    * Yes    * Yes
    * No    * No
 </doodle> </doodle>
  
 +The votes started on 2016-06-12 at 19:00 UTC and ended on 2016-06-19 at 19:00 UTC.
  
 ===== Patches and Tests ===== ===== Patches and Tests =====
 +
 +The initial PR can be found here:
  
   * https://github.com/php/php-src/pull/1455   * https://github.com/php/php-src/pull/1455
 +
 +Note that the PR is currently outdated but it will be updated if the RFC is accepted.
  
 ===== Implementation ===== ===== Implementation =====
 +
 +Merged into php-src for PHP 7.1: https://github.com/php/php-src/commit/75b86a2
 +
 After the project is implemented, this section should contain  After the project is implemented, this section should contain 
-  - the version(s) it was merged to 
-  - a link to the git commit(s) 
   - a link to the PHP manual entry for the feature   - a link to the PHP manual entry for the feature
  
 ===== References ===== ===== References =====
  
-https://marc.info/?l=php-internals&m=143786181229714&w=2+  * https://marc.info/?l=php-internals&m=143786181229714&w=2 
 +  * https://bugs.php.net/bug.php?id=71473 
 +  * http://blog.reverberate.org/2016/02/06/floating-point-demystified-part2.html
  
 ===== Rejected Features ===== ===== Rejected Features =====
-Keep this updated with features that were discussed on the mail lists.+ 
 +None 
rfc/precise_float_value.1438755552.txt.gz · Last modified: 2017/09/22 13:28 (external edit)