PHP floating point precision

Embed Size (px)

DESCRIPTION

How to get the precision you want from PHP floating point variables

Citation preview

  • 1. Perfectly Problematic PHP Precision Or Dude Where's My Digits? About the precision of floating point numbers in PHP By Daniel Rhodes Of Warp Asylum Ltd http://www.warpasylum.co.uk

2. Floating what now? A floating point number is how we store fractional numbers in computing For example, 1.23, 3.1415, 0.05 all need to be stored as floating point numbers The decimal point floats as we can store different sized whole parts and different accuracies of the fractional part. For example 34535345345.2 or 2.6745354677 3. OK, so how do we do this in PHP? Well, our first little problemette is that PHP is mostly untyped. The same PHP variable can hold a string or a number. The variable will be treated as numbery if it looks like a number, else it will be treated as stringy. This is actually quite useful given that Hyper TEXT transfer protocol is PHP's biggest context. But in our case... 4. Floating point variables $float = (float) 3.1415; //cast from numeric $float = (float) '3.1415'; //cast from string $float = floatval(3.1415); //get float value $float = floatval('3.1415'); //get float value $float = '3.1415'; settype($float, 'float'); //set as After any of the above, $float will be internally represented as an actual floating point number 5. Advanced problems OK, that's the basics finished. But there's another, less obvious problem to be aware of. Let's say we want to add metrics recording (of every button and link clicked for example) to our website, and we want to save each click event with its timestamp in our NoSQL database... 6. UNIX timestamp with microseconds According to php.net, microtime(true) will return the current UNIX timestamp down to microsecond (ie. 6 digit) accuracy as a float. Let's give it a whirl: $ts = microtime(true); echo $ts; Gives: 1379523470.4252 This is not 6 digit accuracy! 7. Dude where's my digits? The good news is that internally we do indeed have 6 digits The bad news is that, because we used echo, our float got converted to a stringy format which means that some dark, mysterious forces come into play. Namely... The precision directive in php.ini [or ini_set()] 8. Peculiar PHP precision The little-know precision directive sets the maximum precision of floats when echoed or stored as stringy. The current default is 14 (2 less than we need for microsecond timestamps) Kicks in when you (or PHP!) echo(), print(), cast as string or json_encode() a float variable Set it in php.ini or with ini_set('precision', 16) 9. Increasing the precision OK, let's try our timestamp example again but with increased precision: ini_set('precision', 16); $ts = microtime(true); echo $ts; Gives: 1379525237.5543571 6 digits! 10. An interesting exception As mentioned, echo() and json_encode() etc will use the precision directive BUT the stringy sprintf() family of functions do not use the precision directive Let's try it: $ts = microtime(true); echo sprintf(%f, $ts); Gives: 1379534265.381266 Nice! 11. About sprintf() sprintf(%f, $x) defaults to 6 decimal digits But we can specify the precision with, for example, sprintf(%0.8f, $x) for 8 decimal digits So, if appropriate, we can use this instead of tinkering with the precision directive 12. Summary Floating point variables will have precision applied when made stringy by whatever means precision specifies the total number of digits to print before and after the point precision is easily changed in php.ini or with ini_set() sprintf() [and related] does not apply precision and is configurable per use 13. Further reading http://php.net/manual/en/language.types.type-juggling.php http://www.php.net/manual/en/language.types.float.php http://floating-point-gui.de (float arithmetic) http://www.php.net/manual/en/ini.core.php#ini.serialize-precision http://www.leaseweblabs.com/2013/06/the-php-floating-point-precision-is-wrong-by-default