PHP Rounding Anomaly? [duplicate] - php

This question already has answers here:
PHP round half up doesn't work
(3 answers)
Closed 6 years ago.
Not sure if this is normal but personally, I think it is not giving me the right result. Lets look at this example
$a = 3.32475;
$b = round($a,2,PHP_ROUND_HALF_UP);
I was expecting 3.33 but instead I get 3.32. Am I missing something here? Is the rounding function only literally uses the 3rd decimal point value instead of the whole value, and then rounding it up?
What I was expecting was something like this:-
- 3.32475
- 3.3248
- 3.325
- 3.33
Am I doing something wrong here? is there a better way for me to get an accurate rounding base on the whole value rather than the 3rd decimal point?
Thanks.

It would round up to 3.33 anything >= 3.325. Your value is less than that, so it rounds down to 3.32.
As stated in the docs, PHP_ROUND_HALF_UP means:
Round val up to precision decimal places away from zero, when it is half way there. Making 1.5 into 2 and -1.5 into -2. [Emphasis added]
If you want to force it to "round" up, use ceil() instead of round():
$a = 3.32475;
$b = ceil($a * 100) / 100;
This finds the "ceiling" value of 332.475, i.e., 333, then divides that by 100 to give 3.33.
Also, be aware that rounding never actually works the way you described (rounding digits one at a time) unless you write a special routine to do that (and I can't think of any real-world reason you would want to do so).

Related

how to prevent float variables displaying as scientific notation when printing [duplicate]

This question already has answers here:
Why is PHP printing my number in scientific notation, when I specified it as .000021?
(7 answers)
Closed 10 months ago.
I am using "(float)$val" for some calculation, but for some decimal value like -0.00000025478625
(float)-0.00000025478625 is resulting to -2.5479E-70,
i need the value same as that of -0.00000025478625, without affecting other scenarios.
how to prevent this conversion ?
I think you misunderstand the representation of your float. The value -2.5479E-70 actually is still a float value in scientific representation.
What this actually means is that your value is very small, so for readability reasons it is represented in this format. To read it you may replace the E with an multiplication of the following number to the power of 10 -2.5479 * 10^(-70). So this means that your floating point number is prepended with 70 zeros (which I wont write down here).
As example -5.47E-4 would be -5.47 * 10^(-4) which is the same as -5.47/10000 resulting in -0.000547.
Also, for printing your value was rounded. Internally it still uses the exact value. So if you use this number in further evaluations you do not lose any accuracy.
This is the scientific notation of float number. So, if you want to format then you should use number_format() function.
Below example the second parameter will tell at what precision do you need.
So, as per your example you should use 14.
Try this:
$var = number_format((float)-0.00000025478625, 14);
print($var);
Something to add to Manish's answer.
Please note that floating point numbers in PHP have limited precision:
For example:
<?php
echo number_format((float) 0.0000000000000000000000004, 50);
or even
<?php
printf('%f15.50', (float) 0.0000000000000000000000004);
You'd get something like this (depends on the system):
0.00000000000000000000000040000000000000001539794790
which is not exactly the original floating point number to print.
You can never count on the accuracy of floating point number. The best way to deal with them is to always store them as string until you need some calculation done.

What is the purpose of greater than or equal to?

>=
Why use the above and not just the greater than but with one number less.
For example:
> 8
Is the same as
>= 9
I know it's not a huge deal, but it just seems to serve no purpose to me.
FYI I'm still fairly beginner with PHP so I'm asking a lot of 'why's' at the moment
Let's use an example where you want to cover the left side of the equation being greater than or equal to data input by a user or coming from another variable (something where you don't actually have a hardcoded number).
Your options are $value >= $input or $value > ( $input - 1 )
Very often when writing software, you won't have a specific number in mind. Values change. Writing production software is more about logic than it is math.
How would you represent any number greater or equal to 1? If your logic was true, then $value > 0 would be enough, but if we are dealing with decimals, then 0.95 would be greater than 0 and this 0.95>0 would equate to TRUE while 0.95 >= 1 would equate to false.
You might then follow up your question with "What if we are only using integers and not decimals?". Then in that case mathematically you are correct in assuming that the logic would be the same in most situations (I say most because to say something is ALWAYS true in programming is a dangerous statement to make).
However, in practical terms, >= is more visual and clear to most, and if for some reason you had to change your code in the future to take decimals into account, your site would have a costly error.

php calculation with decimal(10,3) results in exponential sometimes

In my php script I do a calculation of entries from a MySQL db. The concerning fields in the db are defined as decimal(10,3). It's an accounting plattform where I have to check if in every entry debit = credit.
I do this with the following operation:
$sumupNet = 0;
$sumup = 0;
foreach($val['Record'] as $subkey => $subval)
{
$sumupNet = $sumupNet + $subval['lc_amount_net'];
$sumup = $sumup + $subval['lc_amount_debit'] - $subval['lc_amount_credit'];
}
Now we say every entry is correkt, then $sumupNet and $sumup results in 0. In most cases, this works. But in some cases the result is something like this: -1.4432899320127E-15 or this -8.8817841970013E-15. If I calculate this values manually, the result is 0. I guess (not sure) that the above results are numbers near 0 and are outputted in the form of exponential.
So I think I have to convert something or my calculation is wrong. But what? I tried floatval() at some points but didn't work. If anybody has a hint, I thank you very much.
You're getting this because you are doing math with floating-point values. Read some theory about it.
You really don't want to calculate money like that as you might get weird rounding problems that you can't really do anything to fix.
For PHP, there are plenty of libraries that help you evade the problem, such as BC Math, or GMP.
Other solution would be to calculate all of the values using the smallest monetary value that the currency has (like cents) so you are always using integers.
These are rounding problems. These are perfectly normal when we are talking about floats. To give you an everyday example,
1/3 = 0.3333333333333333...333333333...3333...
Reason: 10 is relative prime with 3. You might wonder where is 10 coming from. We are using 10-base for numbers, that is, whenever we speak about a number, its digits represent 10-base exponential values. The computer works with binary numbers, that is, 2-base numbers. This means that division with such numbers often result in endless sequences of digits. For instance, 1/3 as a binary number looks like this:
0.010101010101010101010101010101010101010101010101010101...
Decimal types are representing decimal numbers, that is, 10-base numbers. You use three digits for the part after the . Let's supose your number ends like this:
.xyz
this means:
xyz / 1000
However, 1000 can be divided with the following prime numbers:
2 and 5.
Since 5 is relative prime with 2, whenever you are representing the result of a division by 5 as a binary number, there is a potential that the result will be an endless cycle of digits. 1/5 as a binary number looks like this:
0.0011001100110011001100110011001100110011001100110011...
Since a computer cannot store endless digits, it has to round the number, that is, find a number close to its value which can be represented in an easier manner. If the number a is rounded to b and the two numbers are not equal, then a certain amount of precision is lost and this is the reason of the bug you have mentioned.
You can solve the problem as follows: when you select the values from the database, multiply them by 1000 (thus, converting them into integers) and then check the operations. At the end, divide by 1000.

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

How can i make the php cos function return the correct value?

I've tried
$x = cos(deg2rad($angle));
but it returns 6.12323399574E-17 when the angle is 90 degrees instead of 0.
I read that this is a floating point problem, but is there a workaround?
6.1E-17 is almost zero anyway[*]. If you need to actually compare the result to zero, in floating point math you should check that it's within a certain tolerance of the desired value, since most numbers can't be represented correctly.
$x = cos(deg2rad($angle));
$is_zero = (abs($x) < 1e-10);
Strictly speaking, of course, zero is actually a number that can be represented correctly in floating point. The real problem is that pi / 2.0 can't be, so the input to your cos function isn't "correct".
[*] To put that in context, taken as a proportion of 1 AU (the average distance from the Sun to the Earth) it is equivalent to 0.0092 millimeters, or about a tenth of the average width of a human hair...
6.12323399574E-17 is an extremely small floating point number (17 zeros after the decimal point followed by a 6), almost indistinguishable from 0. If you are dealing with floating points (as any cosine function must), you can't avoid issues like this. If you need to compare a floating point number to zero, you must do it with error bars (i.e. is this number within a certain range of values around zero), not absolute comparison, even with simpler functions than cosine. If you just need to do some math with the result (add it, multiply it, whatever), it will behave almost exactly like zero so you won't need to worry.
Try this
$x = round(cos(deg2rad($angle)), 3);

Categories