Does the value of floatval('a.bc') exactly equals to abc/100? - php

For example, I know 0.1 is not accurate due to floating point rounding error, however, I have another question: does
$num=floatval('0.1');
exactly equals to
$num=1/10
? Do 2 $num above rounds to the same value finally?
or in general, does
$num=floatval('a.bc');
exactly equals to
$num=abc/100;
(which abc are digits, and abc are integers)?

To my knowledge, PHP does not have an official standard. Such documentation as there is does not fully specify the semantics of floating-point operations. It does not specify which floating-point representation or format implementations must use.
That said, good practice in floating-point operations is for an operation to produce a result as if the result were first computed with infinitely precise arithmetic (exact mathematics) and then rounded according to the rounding rule in effect. (Most commonly, the rule used is to round to the nearest representable value and to round ties to the nearer value with an even low digit.)
If that rule is used, then the operation of converting the decimal numeral a.bc to floating-point should yield exactly the same result as dividing the number abc by 100. This is because the exact mathematical result of interpreting a.bc as decimal equals the exact mathematical result of dividing abc by 100, so they both produce exactly the same result before rounding, and rounding should round them to the same representable value.
In some programming languages, such as C and C++, floating-point expressions are permitted to be evaluated with more precision than the nominal type, and floating-point expressions are permitted to be contracted, meaning that multiple operations can be combined into a single operation that would be equivalent mathematically.
An example of the former is that a programming language implementation might use double for doing arithmetic with values that are nominally of type float. If a PHP implementation did this, then a.bc might effectively have a different value from abc/100 because one is computed with float precision while the other is computed with double precision. If a particular PHP implementation is implemented using C or C++, this variation in precision could happen because the underlying C or C++ implementation does it, as permitted by the C and C++ standards.
An example of the latter is that, when computing a*b + c, some C or C++ implementations may choose to use an instruction that produces a result as if a*b + c were fully computed with exact mathematics and then rounded to the nearest representable value instead of first computing a*b and rounding it, then adding c to the rounded result and rounding a second time. I have not seen indication that PHP implementations do this. However, if they do, then it is possible that interpreting .abc as a decimal numeral and dividing abc by 100 may appear to have different results in some circumstances where extra precision is used or where they are combined with other operations.
Note that, if an implementation is using extra precision and/or contractions, the problem is not just that a.bc might have different value than abc/100. The expression abc/100 might appear to have different values from itself in different situations, as the implementation might use different precisions in different situations.

Related

Floating point precision for currency: using "round" function in MySQL query vs PHP vs Javascript

I am working with dollar amounts. In MySQL database the following fields fee and rate(percentage) are DECIMAL type with 2 decimal precision.
SELECT ROUND(fee * (1- rate/100))),2 ) as profit
from products
Since query is just returning the values instead of saving them in variables, does the precision problem* still exist (that comes with PHP or JS)? If so is it best to round the floating point number in PHP or JS?
*Yes I mean precision issue that occurs when saving double, e.g., 1.5 may be saved as 1.49999999
Others may have alluded to this, but I wanted to let you know my system for handling money calculations in PHP.
I use integers. The thing is that I have each increment represent the highest precision I need. For most of my applications, this is hundredths of a dollar, or one cent. However, you can have it be millionths or whatever you need.
So in practice, with the precision being in hundredths, $.01 is represented as 1, $.10 is represented as 10, $1.00 is represented as 100, and so forth. This really gets rid of the rounding issue as you are going to be manipulating integers only, since the decimal part of any computation will be truncated. This is OK, though, since the integer represents the finest precision you need.
Admittedly, this takes a bit more developing to handle, but rounding should not be one of the issues that crop up.

PHP & Base 2. Which Floats give a precise Value?

Apologies for my poor maths skills, I've tried to understand this to answer my own query but I'm not convinced.
We all know that PHP doesn't store Floats in base 10 but base 2.
I have a series of calculations that are using 0.5 as the only float, and in trying to understand if they will be stored as 0.500001 or 0.4999999 (for rounding purposes there is a big difference!!!) I have come to understand that 0.5 will be stored precisely in base2.
My queries are
A Have I understood this correctly?
B What other floats are stored precisely in base2? eg 0.25?
Any multiple of 1/pow(x, 2) can be precisely represented as a float.
That means x/2, x/4, x/8, x/16 ...ect. can be accurately represented.
For more information on how floating point numbers are store see http://kipirvine.com/asm/workbook/floating_tut.htm
Gmp is a good library for high precision math.
PHP is not required to use binary floating-point. It depends on the system.
Many systems use IEEE-754 binary floating-point (sometimes incompletely or with modifications, such as flushing subnormal numbers to zero).
In IEEE-754 64-bit binary floating point, a number is exactly representable if and only if it is representable as an integer F times a power of two, 2E, such that:
The magnitude of F is less than 253.
–1074 ≤ E < 972.
For example, ½ equals 1•2–1. 1 is an integer under the integer limit, and –1 is an exponent within the exponent limits. So ½ is representable.
253+1 is not representable. As it is, it is an integer outside the integer limit. If you try to scale it by a power of two to bring it within the limit, you get a number that is not an integer. So there is no way to represent this value exactly in IEEE-754 64-bit binary floating-point.
1/3 and 1/10 are also not representable because no matter what power of two you scale them by, you will not produce an integer.

Weird PHP floating point behavior

Having weird problem:
$testTotal = 0;
foreach($completeBankArray as $bank){
var_dump($testTotal);
echo " + ";
var_dump(floatval($bank["amount"]));
echo " = ".(floatval($testTotal) + floatval($bank["amount"]))."</br>";
$testTotal = floatval(floatval($testTotal) + floatval($bank["amount"]));
And this is output I get:
------------------//--------------------
float(282486.09) + float(15) = 282501.09
float(282501.09) + float(3.49) = 282504.58
float(282504.58) + float(22.98) = 282527.55999999
float(282527.55999999) + float(5.2) = 282532.76
float(282532.76) + float(39.98) = 282572.73999999
float(282572.73999999) + float(2.6) = 282575.33999999
float(282575.33999999) + float(2.99) = 282578.32999999
------------------//-----------------------
How is this possible, what am I doing wring ?
You aren't doing anything wrong. Floats are notoriously innaccurate. From the docs (In the huge red warning box):
Floating point numbers have limited precision. Although it depends on the system, PHP typically uses the IEEE 754 double precision format, which will give a maximum relative error due to rounding in the order of 1.11e-16. Non elementary arithmetic operations may give larger errors, and, of course, error propagation must be considered when several operations are compounded.
Additionally, rational numbers that are exactly representable as floating point numbers in base 10, like 0.1 or 0.7, do not have an exact representation as floating point numbers in base 2, which is used internally, no matter the size of the mantissa. Hence, they cannot be converted into their internal binary counterparts without a small loss of precision. This can lead to confusing results: for example, floor((0.1+0.7)*10) will usually return 7 instead of the expected 8, since the internal representation will be something like 7.9999999999999991118....
So never trust floating number results to the last digit, and do not compare floating point numbers directly for equality. If higher precision is necessary, the arbitrary precision math functions and gmp functions are available.
Floats are never exact and will diff quite a bit in the long run. If you are working with precision math, please read about the bc library.
Classic numeric precision example. Computers store floating point numbers in binary.
The short answer is that the computer cannot accurately represent some floating point numbers in binary.
The long answer involves moving between numerical bases. If you have a float, you cannot represent it completely in binary unless the denominator contains can be broken into factors that are powers of 2.
The other answers have given some insight into why you get this behaviour with floats.
If you are dealing with money, one solution to your problem would be to use integers instead of floats, and deal with cents instead of dollars. Then all you need to do is format your output to include the decimal.

php's digit precision how to extend it

In JavaScript this math operation returns:
1913397 / 13.054 = 146575.53240386088
but same operation in php returns:
1913397 / 13.054 = 146575.53240386
I guess this is due to rounding.
How can I extend php's precision?
From an article I wrote for Authorize.Net:
One plus one equals two, right? How about .2 plus 1.4 times 10? That equals 16, right? Not if you're doing the math with PHP (or most other programming languages):
echo floor((0.2 + 1.4) * 10); // Should be 16. But it's 15!
This is due to how floating point numbers are handled internally. They are represented with a fixed number of decimal places and can result in numbers that do not add up quite like you expect. Internally our .2 plus 1.4 times 10 example computes to roughly 15.9999999998 or so. This kind of math is fine when working with numbers that do not have to be precise like percentages. But when working with money precision matters as a penny or a dollar missing here or there adds up quickly and no one likes being on the short end of any missing money.
The BC Math Solution
Fortunately PHP offers the BC Math extension which is "for arbitrary precision mathematics PHP offers the Binary Calculator which supports numbers of any size and precision, represented as strings." In other words, you can do precise math with monetary values using this extension. The BC Math extension contains functions that allow you to perform the most common operations with precision including addition, subtraction, multiplication, and division.
A Better Example
Here's the same example as above but using the bcadd() function to do the math for us. It takes three parameters. The first two are the values we wish to add and the third is the number of decimal places we wish to be precise to. Since we're working with money we'll set the precision to be two decimal palces.
echo floor(bcadd('0.2', '1.4', 2) * 10); // It's 16 like we would expect it to be.

MySQL & PHP decimal precision wrong

24151.40 - 31891.10 = -7739.699999999997
I grab these two numbers from a MySQL table with the type as decimal(14,2)
24151.40
31891.10
It is saved exactly as stated above and it echos exactly like that in PHP. But the minute I subtract the second value from the first value, I get a number -7739.699999999997 instead of -7,739.7. Why the extra precision? And where is it coming from?
From an article I wrote for Authorize.Net:
One plus one equals two, right? How about .2 plus 1.4 times 10? That equals 16, right? Not if you're doing the math with PHP (or most other programming languages):
echo floor((0.2 + 1.4) * 10); // Should be 16. But it's 15!
This is due to how floating point numbers are handled internally. They are represented with a fixed number of decimal places and can result in numbers that do not add up quite like you expect. Internally our .2 plus 1.4 times 10 example computes to roughly 15.9999999998 or so. This kind of math is fine when working with numbers that do not have to be precise like percentages. But when working with money precision matters as a penny or a dollar missing here or there adds up quickly and no one likes being on the short end of any missing money.
The BC Math Solution
Fortunately PHP offers the BC Math extension which is "for arbitrary precision mathematics PHP offers the Binary Calculator which supports numbers of any size and precision, represented as strings." In other words, you can do precise math with monetary values using this extension. The BC Math extension contains functions that allow you to perform the most common operations with precision including addition, subtraction, multiplication, and division.
A Better Example
Here's the same example as above but using the bcadd() function to do the math for us. It takes three parameters. The first two are the values we wish to add and the third is the number of decimal places we wish to be precise to. Since we're working with money we'll set the precision to be two decimal palces.
echo floor(bcadd('0.2', '1.4', 2) * 10); // It's 16 like we would expect it to be.
PHP doesn't have a decimal type like MySQL does, it uses floats; and floats are notorious for being inaccurate.
To cure this, look into number_format, e.g.:
echo number_format(24151.40 - 31891.10, 2, '.', '');
For more accurate number manipulation, you could also look at the math extensions of PHP:
http://www.php.net/manual/en/refs.math.php
This has to do with general float / double precision rates, which scientifically relates to 1.FRACTAL * 2^exponential power. Being that there's a prefix of 1, there's technically no such thing as zero, and the closest value you can obtain to 0 is 1.0 * 2 ^ -127 which is .000000[127 0s]00001
By rounding off your answer to a certain precision, the round factor will give you a more precise answer
http://dev.mysql.com/doc/refman/5.0/en/mathematical-functions.html#function_round

Categories