PHP and unit testing assertions with decimals - php

I have a method that returns a float like 1.234567890.I want to test that it really does so. However, it seems that this returned float has different precision on different platforms so how do I assert that the returned value is 1.23456789? If I just do:
$this->assertEqual(1.23456789, $float);
Then that might fail on some platforms where there is not enough precision.

So far it hasn't been mentioned that assertEquals supports comparing floats by offering a delta to specifiy precision:
$this->assertEquals(1.23456789, $float, '', 0.0001);
Thanks to #Antoine87 for pointing out: since phpunit 7.5 you should use assertEqualsWithDelta():
$this->assertEqualsWithDelta(1.23456789, $float, 0.0001);

As an update to #bernhard-wagner answer, you should now use assertEqualsWithDelta() since phpunit 7.5.
$this->assertEqualsWithDelta(1.23456789, $float, 0.0001);

In general, it's a bad idea to test built-in floats for equality. Because of accuracy problems of floating point representation, the results of two different calculations may be perfectly equal mathematically, but different when you compare them at your PHP runtime.
Solution 1: compare how far apart they are. Say, if the absolute difference is less than 0.000001, you treat the values as equal.
Solution 2: use arbitrary precision mathematics, which supports numbers of any size and precision, represented as strings.

For greater accuracy you may consider using BCMath.

Alternatively of using bcmath() you can also set the default precision, like this:
ini_set('precision', 14);

Related

In PHP, can ceil() ever round up an even division?

I have read a bunch of questions and comments but haven't seen this mentioned, and since others may have the same question, I'm posting it here.
Considering floating point errors, is it ever possible to get a result one point higher than it should be from a ceil($a / $b) where the rest of $a / $b is 0?
If so, since I'm working with positive integers higher than 0, perhaps I should write my_ceil() where I check for $a % $b first and if it's not 0, add 0.1 to $a before calling the built-in function…
If what you appear to be asking is how to use floating points including their correct non-absolute errors in deciding a ceil outcome, the following should guide you:
1) http://www.php.net/manual/en/language.types.float.php
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 [my emphasis]. If higher
precision is necessary, the arbitrary precision math functions and gmp functions are available.
Read up
Arbitrary precision math functions : http://uk1.php.net/manual/en/ref.bc.php
And gmp : http://uk1.php.net/manual/en/ref.gmp.php
2) You are caring about floats (as the single input value) when using ceil($float) when ceil will only ever round up to the nearest integer so whatever the floating value is, is irrelevant. You may be considering using the round() function instead. which has it's own ways of dealing with the above floating point inaccuracy issue.
http://php.net/manual/en/function.round.php
To answer the original question of Considering floating point errors, is it ever possible to get a result one point higher than it should be from a ceil($a / $b) where the rest of $a / $b is 0? the answer is YES because of two points:
Float is potentially significantly inaccurate in base10 numbers, and,
You are using the wrong function to get the output you want to the precision you need. In this situation using multiple float numbers you want a function that have built in precision required, such as gmp or maths functions .
Looking at the ceil source code on github, it seems that it is dependent on your C library's ceil function.
Edit: To answer the question – YES, it (probably) can.
I guess this doesn't really have anything to do with ceil() but rather with if the division in question returns a float or integer type value.
According to http://php.net/manual/en/language.operators.arithmetic.php …
The division operator ("/") returns a float value unless the two operands are integers (or strings that get converted to integers) and the numbers are evenly divisible, in which case an integer value will be returned.
So using at least one float value could produce a ceil() "error", but I should be fine with two integers.

Convert string to float without losing precision

var_dump((float)'79.10') returns me 79.09999999999999. I've tried a million ways to try and round this value up to the original 79.10 as a float (number_format, round), and I can't find a way to do it.
Is there any way I can get a float value of 79.10 from the original string?
No, because 0.1 (and, by extension, 79.1) is not actually representable as a float (assuming IEEE-754 single or double precision encoding). 0.1 in that encoding has an infinitely recurring fractional part:
1001 1001 1001 1001 ...
You'll either have to leave it as a string or accept the fact that the encoding scheme does not have infinite precision and work around it.
An example of the latter is to only output the numbers to a certain precision, such as two decimal digits, and to make sure that (in-)equality comparisons use either absolute or relative deltas to compare numbers.
When you're adding numbers, it takes quite a few operations for the imprecision effects to become visible at the hundredths level. It's quicker when multiplying but still takes a while.
While paxdiablo is right and working with floats does not have infinite precision, I've discovered that it is indeed possible to represent 79.10 as a float by first adjusting PHP's precision setting:
ini_set('precision', 15);
After that, var_dump((float)'79.10') correctly returns a float of 79.1. The different results that everyone is seeing on their own machines seems to be a result of everyone having different precision values set by default.
This is impossible as a float because it does not offer enough precision (see here for more information)
Now, in most languages you could cast it to a double... Unfortunately, in PHP, float and double use exactly the same underlying datatype so in order to get the value you want, you would have to recompile PHP.
Your best option would be to use something like the PHP BCMath module for arbitrary precision.

Why is MySQL is returning some floats in scientific notation, but not others?

I'm writing a PHP script that looks through a DB table of float values, that tend to be somewhat small, such as:
0.00052
0.00134
0.00103
0.00149
0.00085
0.00068
0.00077
0.00088
0.00169
0.00063
For reasons unknown to me, some values appear in the DB in scientific notation, such as:
1.12305e-06
The table is set to float, and I've tried all manner of functions in PHP to force the numbers to display as decimal, to no avail. Try as I might, I'm unable to get this table of numbers to be consistently decimal in all cases.
Any suggestions on how to resolve this? Have tried typcasting to (float) and using number_format() and several other options, but no change every time.
There seems to be a six digit limit on what is shown out of the CLI (and probably elsewhere). The example you have is 1.12305e-06 which is 0.00000112305 which would be shown as 0.00000 - though clearly it isn't zero.
If you are insisting on using floats or doubles, you will have to force them out using something like round(columnName,5) to force the display in a decimal value. Otherwise, maybe switch to a decimnal data type.
From http://dev.mysql.com/doc/refman/5.0/en/floating-point-types.html
Because floating-point values are approximate and not stored as exact values, attempts to treat them as exact in comparisons may lead to problems. They are also subject to platform or implementation dependencies. For more information, see Section C.5.5.8, “Problems with Floating-Point Values”
Also see this thread on the mysql forums about this exact issue.
The solution in my case turned out to be changing from float to decimal type in the database, so thanks to Romain for the comment that led me to look into that solution!

How to parse long value in php?

Need something like intVal() but for bigger numbers, for example: 100001416147426.
Is there a function like this in php? Can't find it.
You should use BC Math, it is designed to numbers of any size with PHP.
The BC Math extensions offers several mathematic functions like:
bcadd Add two arbitrary precision numbers
bccomp — Compare two arbitrary precision numbers
bcsqrt Get the square root of an arbitrary precision number
...
On the PHP documentation site there is a small code example by Charles to round a long number!
consider
$x = (double) "100001416147426";
var_dump($x);
output:
float(1.0000141614743E+14)
coding standard in the past (since C) has been to follow the number with an L/l
$x = 100001416147426L;
This cue's the parser to allocate 64 bits in order to read the number out of the script to compile.
But unless you are running the php x64 this will be useless. Other wise you will have to build it out of a big_number component. Once in a x64 environment intval will automatically expand to a long when exceeding a 32-bit int.

PHP Integer Problem

When I print this number in php 137582392964679 I get this as the output 1.37582392965E+14
All I am doing is a simple
print 137582392964679;
Anyone know why it's doing this? It's as if it's converting to an exponential number automatically. Someone said it's because I'm on a 32 bit machine. If that's the case how can I get around this problem?
Thanks
Check the const PHP_INT_MAX. You're likely over the max, which is typically around 2 billion for a 32bit system.
The maximum number you can store in a signed integer on a 32-bit machine is 2147483647. You can store numbers larger than this in a float but you risk losing some precision.
If that's the case how can I get around this problem?
You probably want to use a big number library. Try GMP:
$sum = gmp_add("123456789012345", "76543210987655");
echo gmp_strval($sum) . "\n";
Result:
200000000000000
Another alternative you could use is BC Math.
If you don't need to do any calculations with these numbers, but just store tham correctly, then store them as strings rather than integers.
I am on a 64 bit machine and it does the same thing. You might want to try using: print number_format(137582392964679);
That number is too big to fit into a 32-bit integer, so yes, it is converting to a floating point type automatically. How to get around it depends on the requirements of your system. If you aren't going to do any arithmetic then just store it as a string. If precision isn't overly important then you could leave it as a float and format it using printf. If precision is important and you can upgrade to 64-bit that should fix it, if you can't upgrade and you need an integer then you could look into using the BC Math PHP extension.
The manual clearly says:
If PHP encounters a number beyond the
bounds of the integer type, it will
be interpreted as a float instead.
Also your number cannot be represented accurately because of inherent floating point limitations, hence it is being approximated.

Categories