Multiplication error in php - php

I'm building a site where people can exchange coins (site currency) into Bitcoin. The problem I'm having is that for some reason when I multiply the $btcprice with 3 or less the echo is really weird... for your sake this is the code that matters:
<?php
// get 0,01 usd in bitcoins into a variable
$btcprice = file_get_contents('https://blockchain.info/tobtc?currency=USD&value=0.01');
$valueInBTC = 4 * $btcprice;
echo $valueInBTC;
?>
Anything that's 4 or higher will work, but if you try to multiply this with 3 or less it gets weird. For example this:
<?php
// get 0,01 usd in bitcoins into a variable
$btcprice = file_get_contents('https://blockchain.info/tobtc?currency=USD&value=0.01');
$valueInBTC = 3 * $btcprice;
echo $valueInBTC;
?>
Will echo 7.959E-5
I just don't understand what the problem is...

The result you are getting is not an error. It is simply in a formatting you don't expect / know yet. 7.959E-5is exactly the same as 0.00007959 it is just a different way of writing it down. Think of it as 7.959E-5 = 7.959 × (10 ^ (-5)) = 0.00007959. It is called Scientific notation (E notation). In cumputation / science this notation is used, because you can show very large or very small (as in your case) numbers with less digits (it is just shorter to write).
To get the number in other formattings use the php function sprintf().
As you are handling bitcoin values, you shouldn't be formatiing the numbers until just for output. With bitcoins you always deal with very small numbers and you will soon meet precision problems if you try and calculate with formatted floating point numbers.

In most cases this is a formatting issue. You can simply use printf
printf("%.2f",$valueInBTC);
Another good option is to use number_format();
Eg:
$number = 1234.5678;
// english notation without thousands separator
$english_format_number = number_format($number, 2, '.', '');
// 1234.57

Related

PHP is converting decimal to exponential value [duplicate]

In PHP I have the following code:
<?PHP
$var = .000021;
echo $var;
?>
the output is 2.1E-5 !
Why? it should print .000021
Use number_format() to get what you're after:
print number_format($var, 5);
Also check sprintf()
2.1E-5 is the same number as 0.000021. That's how it prints numbers below 0.001. Use printf() if you want it in a particular format.
Edit If you're not familiar with the 2.1E-5 syntax, you should know it is shorthand for 2.1×10-5. It is how most programming languages represent numbers in scientific notation.
Use number_format or sprintf if you want to see the number as you expect.
echo sprintf('%f', $var);
echo number_format($var, 6);
To show a number up to 8 decimal spaces, without extra zeroes to the right (as number_format does, which can be annoying), use this:
echo rtrim(rtrim(sprintf('%.8F', $var), '0'), ".");
In general, a number is a number, not a string, and this means that any programming language treats a number as a number. Thus, the number by itself doesn't imply any specific format (like using .000021 instead of 2.1e-5). This is nothing different to displaying a number with leading zeros (like 0.000021) or aligning lists of numbers. This is a general issue you'll find in any programming language: if you want a specific format you need to specify it, using the format functions of your programming language.
Unless you specify the number as string and convert it to a real number when needed, of course. Some languages can do this implicitly.
The previous answers responded to OP question, but none offered the code to do it.
Use this function to format any number with E- format.
function format_amount_with_no_e($amount) {
$amount = (string)$amount; // cast the number in string
$pos = stripos($amount, 'E-'); // get the E- position
$there_is_e = $pos !== false; // E- is found
if ($there_is_e) {
$decimals = intval(substr($amount, $pos + 2, strlen($amount))); // extract the decimals
$amount = number_format($amount, $decimals, '.', ','); // format the number without E-
}
return $amount;
}
Please note the function will always return a string.
Programming languages have different methods for storing numbers in memory. This is determined by the type of number that is being used. In your case, you have a floating point number (a fraction) that is to large to be stored as a fixed point number ( fractions are stored in this manner depending on their size).
This is a very important feature especially when working with very large or very small numbers. For instance, NASA or spaceX uses special storage methods for its calculations to ensure that the rockets the re-enter earths orbit land where they should.
Also, different storage methods take up different amounts of memory. However, the solution provided above should work. Just remember round off errors might occur with very big or small numbers.

How to remove decimal in PHP?

I am currently echoing a number like this:
<?php echo $enterprise_platinum_price ?>
The problem is that the number normally has a decimal place such as 19.44 or 13.353
I need to display the full number but without the decimal point or any of the numbers behind it. I know that you can round to the full number in PHP but I don't ever want it to round up. If the only way to do this is with rounding, I need it to always round down...
Use the math floor function to round downward.
$a = floor(1.99); // $a = 1
You can also do it without rounding using number_format. There's many, many more ways, but I think the floor() option is the shortest for you.
$a = number_format(1505.99, 0, '', ''); // $a = 1505

PHP Long string. Output it all

So I got a really long string, made by a calculator.
$string='483451102828322427131269442894636268716773727170';
$result=(8902543901+$string)*($string/93.189)/($string)+55643907015.57895461;
echo $result;
This outputs 5.1878558931668E+45
So now my question is. How can I output the whole string, without that nasty E+45?
PHP on a 64 bit machine can only accurately calculate number up until 9223372036854775807. As soon as you calculate with numbers higher than that, php will switch to floats which may loose some of it's precision, especially when you use divisions.
There's an extension for php that will allow you to make calculations based on string, called BCMath.
Example:
$string = '483451102828322427131269442894636268716773727170';
$result = bcadd($string, 8902543901);
echo $result;
bcadd() is for additions, bcdiv() for divisions and bcmul() for multiplying.
You can't print exact value because you are using calculation, so this $string becomes a number (float in this case) and all numbers have limited precision.
If you want to do operations on big numbers you should use BCMath
However if you want to display it without scientific notation you can do it using:
echo sprintf("%f",$result);
or
echo sprintf("%.0f",$result);
if you want to omit decimal part

Why is PHP printing my number in scientific notation, when I specified it as .000021?

In PHP I have the following code:
<?PHP
$var = .000021;
echo $var;
?>
the output is 2.1E-5 !
Why? it should print .000021
Use number_format() to get what you're after:
print number_format($var, 5);
Also check sprintf()
2.1E-5 is the same number as 0.000021. That's how it prints numbers below 0.001. Use printf() if you want it in a particular format.
Edit If you're not familiar with the 2.1E-5 syntax, you should know it is shorthand for 2.1×10-5. It is how most programming languages represent numbers in scientific notation.
Use number_format or sprintf if you want to see the number as you expect.
echo sprintf('%f', $var);
echo number_format($var, 6);
To show a number up to 8 decimal spaces, without extra zeroes to the right (as number_format does, which can be annoying), use this:
echo rtrim(rtrim(sprintf('%.8F', $var), '0'), ".");
In general, a number is a number, not a string, and this means that any programming language treats a number as a number. Thus, the number by itself doesn't imply any specific format (like using .000021 instead of 2.1e-5). This is nothing different to displaying a number with leading zeros (like 0.000021) or aligning lists of numbers. This is a general issue you'll find in any programming language: if you want a specific format you need to specify it, using the format functions of your programming language.
Unless you specify the number as string and convert it to a real number when needed, of course. Some languages can do this implicitly.
The previous answers responded to OP question, but none offered the code to do it.
Use this function to format any number with E- format.
function format_amount_with_no_e($amount) {
$amount = (string)$amount; // cast the number in string
$pos = stripos($amount, 'E-'); // get the E- position
$there_is_e = $pos !== false; // E- is found
if ($there_is_e) {
$decimals = intval(substr($amount, $pos + 2, strlen($amount))); // extract the decimals
$amount = number_format($amount, $decimals, '.', ','); // format the number without E-
}
return $amount;
}
Please note the function will always return a string.
Programming languages have different methods for storing numbers in memory. This is determined by the type of number that is being used. In your case, you have a floating point number (a fraction) that is to large to be stored as a fixed point number ( fractions are stored in this manner depending on their size).
This is a very important feature especially when working with very large or very small numbers. For instance, NASA or spaceX uses special storage methods for its calculations to ensure that the rockets the re-enter earths orbit land where they should.
Also, different storage methods take up different amounts of memory. However, the solution provided above should work. Just remember round off errors might occur with very big or small numbers.

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