number management with 16 decimals - php

I need to use numbers with 16 decimal places in a project and do math operations with them, but I have problems with their handling.
Whatever it does in php, these are truncated if they are too long.
such as if:
$value = 123456789.1234567890;
Printing it with "echo" the result I have on the screen is this: 123456789.12346
Even if the number has few elements in the integer part, the decimal part is truncated
$value = 0.12345678901234567890;
In output I have:
0.12345678901235
What can I do to manage them easily?
Thanks for your help.
I tried using the instruction suggested by GuidoFaecke:
ini_set('precision', '16')
Or:
ini_set('precision', '-1');
But when I use -1 I don't see decimal number. Using 16 I don't see changing showing the number.

Related

PHP: rounding a number into 16 decimal digits

Hi I'm trying to rounding a number into the 16 decimal digits but it only show and doesn't round up till 14 decimal digit.
Here's my attempt:
<?php
$num= 0.16346153846153846;
$round = number_format((float)$num, 17, '.', '');
echo $round * -1;
?>
OUTPUT:
-0.16346153846154
EXPECTED OUTPUT:
0.1634615384615385
I know that float is only 14 decimal digits. Is there any other way around for the 16 decimal digits?
You can set this parameters at run-time. 16 digits is usually the maximum
value on most platforms, larger values giving only meaningless or "fictional"
digits:
ini_set("precision", "16");
See Changing precision level floating-point variables
Recently we experienced an issue with the reading of amount(decimal number) from an excel file which has a decimal number with more than 14 digits after the decimal. The issue is fixed after using the option of an advanced algorithm for converting the decimal number. For this, we need to set,
ini_set('precision', -1);
Ref: http://php.net/precision
Can you try doing this, & see what happens? (May not be the best way of dealing with floats and numbers though)
$num = 0.16346153846153846;
$new_num = $num * -1;
echo number_format((float)$new_num, 17);
Multiply first, then try rounding the result.
number_format and the precision INI setting uses float, which is likely to result in unexpected behaviour if you're rounding to that many decimal digits.
You can alternatively use the PHP decimal extension with $decimal->toFixed(16) or $decimal->round(16) to achieve this with guaranteed accuracy regardless of your INI.

php converting exponetial value having exponent > 12 to decimal

I am reading a value from excel file when it is a number >= 14 digit it convert this to something like 3.5775004173581E+14 I want to get the exact value in decimal like 3.57750041735819 I have tried
(float) 3.5775004173581e+14 O/P 3.5775004173581e+14
intval((float) 3.5775004173581e+14) O/P 740815490
number_format(3.5775004173581e+14) O/P 3.57750041735810
but former return the same string as O/P second one produces some garbage value where as works well for the exponent <14 and the number_format adds traling 0 after 14 digits.
That's because float precision setting is default 14. To change this, use ini_set(), for example.Then you'll be able to get proper values. Sample:
$strVal = "1234567890.123456789";
//float(1234567890.1235), because
//default precision is 14:
var_dump((double)$strVal);
//float(1234567890.123456717)
ini_set('precision', 19);
var_dump((double)$strVal);
This is not only about decimal precision, but about float precision
:
$strVal = "1234567890123456789";
var_dump((double)$strVal);//float(1.2345678901235E+18)
ini_set('precision', 19);
var_dump((double)$strVal);//float(1234567890123456768)
Also, important note - it seems that trying to overcome precision in your case is an attempt to resolve symptoms, not the problem. So you should choose correct data model rather than try to solve this "problem".

Display float value w/o scientific notation

When i make the following multiplication in PHP:
$ret = 1.0 * 0.000000001;
i get the result: 1.0E-9
I want to convert this result into the normal decimal notation, how can i do this?
sprintf('%f',$ret) doesn't work, it returns 0.000000. Overflow?
sprintf('%f',$ret) doesn't work, it returns 0.000000. Overflow?
sprintf works, however you miss some point here.
0.000000 is not overflow. It's just that sprintf for the %f modifier uses 6 digits per default. Also please take care that %f is locale aware, %F is probably better suited.
You might want to use more digits, e.g. let's say 4 000 000 (four million):
$ php -r "printf('%.4000000F', 1*0.000000001);"
Notice: printf(): Requested precision of 4000000 digits was truncated to PHP maximum of 53 digits in Command line code on line 1
Call Stack:
0.0001 319080 1. {main}() Command line code:0
0.0001 319200 2. printf() Command line code:1
0.00000000100000000000000006228159145777985641889706869
As this example shows, there is not only a common value (6 digits) but also a maximum (probably depended on the computer system PHP executes on), here truncated to 53 digits in my case as the warning shows.
Because of your question I'd say you want to display:
0.000000001
Which are nine digits, so you need to write it that way:
sprintf('%.9F',$ret)
However, you might want to do this:
rtrim(sprintf('%.20F', $ret), '0');
which will remove zeroes from the right afterwards:
0.000000001
Hope this is helpful.
You need to add precision specifier (how many decimal digits should be displayed for floating-point numbers). Something like this:
echo sprintf('%.10f',$ret); // 0.0000000010
If you have no idea what number you should specify, just give it a big number and combine it with rtrim().
echo rtrim(sprintf('%.20f', $ret), '0'); // 0.000000001
The code above will strip any 0's from the end of the string.
I suggest the use BCMath for more accuracy when you are calculating with decimal numbers. That makes sure that you actually get the results you want.
To print what you want, you should specify the precision and use %.9f, since it defaults to displaying 6 decimal numbers. That makes it something like this (just like bsdnoobz already said):
sprintf('%.9f',$ret);
To align to your system's settings and limitations, you could use serialize_precision to get the most accurate result possible.
echo rtrim(sprintf('%.'.ini_get('serialize_precision').'f', $ret));
I do not recommend using the non-locale aware %F since your question only makes sense for display purposes. Respecting locale makes for a better UX.

Convert a big integer to a full string in PHP

I've been searching for a while now, but what I can find is not what I search for. I need to convert an integer value, that may be very huge, to a string. Sounds easy: "$var"? No, because this can lead to the E+ representation of the number.
<?php
$var = 10000000000000000000000000;
echo $var."\n";
echo "'$var'\n";
echo (string) $var."\n";
echo strval($var);
?>
1.0E+25
'1.0E+25'
1.0E+25
1.0E+25
How can I make the output be 10000000000000000000000000 instead?
This is not stored as an integer by PHP, but a float, this is why you end up with 1.0E+25 instead of 10000000000000000000000000.
It's sadly not possible to use that as an integer value in PHP, as PHP cannot save an integer of that size. If this comes from database then it will be a string and you can do with it whatever you want. If you store it elsewhere then store it as a string.
Your alternative is to store it as a float and take that into account at all times, though that requires additional conversions and handling in places.
It's also been suggested to use GNU Multiple Precision, but that's not enabled in PHP by default.
$int=gmp_init("10000000000000000000000000");
$string=gmp_strval($int);
echo $string;
UPDATE:
Found the next post:
// strval() will lose digits around pow(2,45);
echo pow(2,50); // 1.1258999068426E+015
echo (string)pow(2,50); // 1.1258999068426E+015
echo strval(pow(2,50)); // 1.1258999068426E+015
// full conversion
printf('%0.0f',pow(2,50)); // 112589906846624
echo sprintf('%0.0f',pow(2,50)); // 112589906846624
Use printf or sprintf.
I'm facing this problem when getting facebook id and find it in MySQL.
And after half hour, i found this work perfectly!
Insert this line to your php script:
ini_set('precision',30);
From: https://forums.phpfreaks.com/topic/125907-solved-convert-big-number-to-string/#entry651084
The integer number you like to express:
$var = 10000000000000000000000000;
is not available on your system. It's too large and therefore PHP converts it into a float which will change the number (32 bit system example):
10000000000000000905969664
Common limits are:
yours : 10 000 000 000 000 000 000 000 000
32 bit: 2 147 483 648
64 bit: 9 223 372 036 854 775 808
The change of the value is called floating point precision, the PHP manual about integers will tell you about the integer limit and the floats page about floating point precision (see the big red warning). Depending on which system you are, you can compile PHP with the ranges your application needs or you must use another datatype, for example with the gmp library which is able to pick strings as integer numbers and handle them.
The following example shows just output, but you can do multiplications etc.:
$r = gmp_init('10000000000000000000000000');
echo gmp_strval($r);
Hope this is helpful.

php intval() and floor() return value that is too low?

Because the float data type in PHP is inaccurate, and a FLOAT in MySQL takes up more space than an INT (and is inaccurate), I always store prices as INTs, multipling by 100 before storing to ensure we have exactly 2 decimal places of precision. However I believe PHP is misbehaving. Example code:
echo "<pre>";
$price = "1.15";
echo "Price = ";
var_dump($price);
$price_corrected = $price*100;
echo "Corrected price = ";
var_dump($price_corrected);
$price_int = intval(floor($price_corrected));
echo "Integer price = ";
var_dump($price_int);
echo "</pre>";
Produced output:
Price = string(4) "1.15"
Corrected price = float(115)
Integer price = int(114)
I was surprised. When the final result was lower than expected by 1, I was expecting the output of my test to look more like:
Price = string(4) "1.15"
Corrected price = float(114.999999999)
Integer price = int(114)
which would demonstrate the inaccuracy of the float type. But why is floor(115) returning 114??
Try this as a quick fix:
$price_int = intval(floor($price_corrected + 0.5));
The problem you are experiencing is not PHP's fault, all programming languages using real numbers with floating point arithmetics have similar issues.
The general rule of thumb for monetary calculations is to never use floats (neither in the database nor in your script). You can avoid all kinds of problems by always storing the cents instead of dollars. The cents are integers, and you can freely add them together, and multiply by other integers. Whenever you display the number, make sure you insert a dot in front of the last two digits.
The reason why you are getting 114 instead of 115 is that floor rounds down, towards the nearest integer, thus floor(114.999999999) becomes 114. The more interesting question is why 1.15 * 100 is 114.999999999 instead of 115. The reason for that is that 1.15 is not exactly 115/100, but it is a very little less, so if you multiply by 100, you get a number a tiny bit smaller than 115.
Here is a more detailed explanation what echo 1.15 * 100; does:
It parses 1.15 to a binary floating point number. This involves rounding, it happens to round down a little bit to get the binary floating point number nearest to 1.15. The reason why you cannot get an exact number (without rounding error) is that 1.15 has infinite number of numerals in base 2.
It parses 100 to a binary floating point number. This involves rounding, but since 100 is a small integer, the rounding error is zero.
It computes the product of the previous two numbers. This also involves a little rounding, to find the nearest binary floating point number. The rounding error happens to be zero in this operation.
It converts the binary floating point number to a base 10 decimal number with a dot, and prints this representation. This also involves a little rounding.
The reason why PHP prints the surprising Corrected price = float(115) (instead of 114.999...) is that var_dump doesn't print the exact number (!), but it prints the number rounded to n - 2 (or n - 1) digits, where n digits is the precision of the calculation. You can easily verify this:
echo 1.15 * 100; # this prints 115
printf("%.30f", 1.15 * 100); # you 114.999....
echo 1.15 * 100 == 115.0 ? "same" : "different"; # this prints `different'
echo 1.15 * 100 < 115.0 ? "less" : "not-less"; # this prints `less'
If you are printing floats, remember: you don't always see all digits when you print the float.
See also the big warning near the beginning of the PHP float docs.
The other answers have covered the cause and a good workaround to the problem, I believe.
To aim at fixing the problem from a different angle:
For storing price values in MySQL, you should probably look at the DECIMAL type, which lets you store exact values with decimal places.
Maybe it's another possible solution for this "problem":
intval(number_format($problematic_float, 0, '', ''));
PHP is doing rounding based on significant digits. It's hiding the inaccuracy (on line 2). Of course, when floor comes along, it doesn't know any better and lops it all the way down.
As stated this is not a problem with PHP per se, It is more of an issue of handling fractions that can't be expressed as finite floating point values hence leading to loss of character when rounding up.
The solution is to ensure that when you are working on floating point values and you need to maintain accuracy - use the gmp functions or the BC maths functions - bcpow, bcmul et al. and the problem will be resolved easily.
E.g instead of
$price_corrected = $price*100;
use $price_corrected = bcmul($price,100);

Categories