I have a simple piece of code as below.
$amount = 447274.44882;
$rate = 0.00001;
echo floatNumber(bcmul($amount, $rate, 8), 8);
This outputs 0.00000000 when it should be 4.47274449. If I change the rate to 0.0001 then it outputs the correct number, anything higher than 4 decimals and it reports 0.
Am I doing something wrong or is this a known limitation or something? Seems quite a big one if that's the case.
If you cast 0.00001 to string using the default settings (and that's what will happen if you feed bcmul with floats since it expects strings) you'll get this:
var_dump( (string)0.00001 );
string(6) "1.0E-5"
It isn't clearly documented but bcmath functions apparently return cast to zero when faced to invalid input:
var_dump( bcadd('Hello', 'world!', 8) );
var_dump( bcadd('33', 'Foo', 8) );
var_dump( bcdiv('33', 'Foo', 8) );
string(10) "0.00000000"
string(11) "33.00000000"
Warning: bcdiv(): Division by zero
NULL
The whole idea of arbitrary precision libraries is to overcome the limitations of base 2 arithmetic and fixed size storage. Thus you'd need this:
var_dump( bcmul('447274.44882', '0.00001', 8) );
string(10) "4.47274448"
This is great to do math with 100-digit numbers but not particularly useful for simple rounding. In fact, the extension doesn't round at all—it merely truncates:
var_dump( bcmul('20.01', '1.444', 3) );
var_dump( bcmul('20.01', '1.444', 2) );
var_dump( bcmul('20.01', '1.444', 1) );
var_dump( bcmul('20.01', '1.444', 0) );
string(6) "28.894"
string(5) "28.89"
string(4) "28.8"
string(2) "28"
Related
I have let's say 0.00001004 or 0.00001
I am trying to choose and how decimal places to prune off and turn both of those so it returns 0.00001 both times.
I do not want it to round the number in anyway.
I've tried this but it is not giving me the desired results.
function decimalFix($number, $decimals) {
return floatval(bcdiv($number, 1, $decimals));
}
echo decimalFix(0.00001, 5); // returns "0"
Does anyone know what I can do? I can't have any rounding involved and I need it to return it as a float and not a string.
I don't know why you're so committed to losing precision, but here's some math to make that particular mistake in the way you wish to make it.
$derp = 0.000016;
function derp_round($derp, $len) {
$mul = pow(10, $len);
return floor($derp * $mul)/$mul;
}
var_dump(
$derp,
number_format($derp, 5),
sprintf("%.5f", $derp),
sprintf("%.5f", round($derp, 5, PHP_ROUND_HALF_DOWN)),
sprintf("%.5f", derp_round($derp, 5))
);
Output:
float(1.6E-5)
string(7) "0.00002"
string(7) "0.00002"
string(7) "0.00002"
string(7) "0.00001"
There's a function that does exactly this in the first comment on the PHP documentation for floor(). I'll copy it here in case it disappears from there, but credits go to seppili_:
function floordec($zahl,$decimals=2){
return floor($zahl*pow(10,$decimals))/pow(10,$decimals);
}
Use it like:
$number = 0.00001004;
$rounded = floordec($number, 5);
var_dump($rounded); // float(0.00001)
Edit: There's a comment further down on that page by Leon Grdic that warns about float precision and offers this updated version:
function floordec($value,$decimals=2){
return floor($value*pow(10,$decimals)+0.5)/pow(10,$decimals);
}
Usage is the same.
This question already has answers here:
Is floating point math broken?
(31 answers)
PHP integer rounding problems
(5 answers)
Closed 8 years ago.
I am trying to get the first decimal place of a float number as an integer by subtracting the integer part, multiplying the remainder with 10 and then casting the result to int or using intval(). I noticed that the result for numbers with x.1 is correctly 1 as float, but after converting it to integer, it becomes sometimes 0, sometimes 1.
I tried to test it with numbers from 1.1 to 9.1:
for ($number = 1; $number < 10; $number++) {
$result = 10 * ($number + 0.1 - $number);
echo "<br/> number = " . ($number + 0.1) . ", result: ";
var_dump($result);
$result_int = intval($result);
var_dump($result_int);
}
Starting with 4.1 as input, the 1 oddly gets converted to 0:
number = 1.1, result: float(1) int(1)
number = 2.1, result: float(1) int(1)
number = 3.1, result: float(1) int(1)
number = 4.1, result: float(1) int(0)
number = 5.1, result: float(1) int(0)
number = 6.1, result: float(1) int(0)
number = 7.1, result: float(1) int(0)
number = 8.1, result: float(1) int(0)
number = 9.1, result: float(1) int(0)
Why at 4.1? That doesn't make any sense to me. Can anyone give me a hint what I am doing wrong?
PS: also tested at http://ideone.com/hr7M0A
You are seeing these results because floating point arithmetic is not perfectly accurate.
Instead of trying to manually get the first decimal point use fmod:
$result = substr(fmod($number, 1) * 10, 0, 1)
My php is a bit rusty, so my syntax in probably off, but shouldn't it be simpler to convert to string and take the rightmost digit ?
sprintf($Str, "%.1f", $number);
$digit=$Str[strlen($Str)-1]; // Last digit
I have a question.
I am using php to generate a number based on operations that a user has specified
This variable is called
$new
$new is an integer, I want to be able to round $new to a 12 digit number, regardless of the answer
I was thinking I could use
round() or ceil()
but I believe these are used for rounding decimel places
So, I have an integer stored in $new, when $new is echoed out I want for it to print 12 digits. Whether the number is 60 billion or 0.00000000006
If i understand correctly
function showNumber($input) {
$show = 12;
$input = number_format(min($input,str_repeat('9', $show)), ($show-1) - strlen(number_format($input,0,'.','')),'.','');
return $input;
}
var_dump(showNumber(1));
var_dump(showNumber(0.00000000006));
var_dump(showNumber(100000000000000000000000));
gives
string(12) "1.0000000000"
string(12) "0.0000000001"
string(12) "999999999999"
well this is what i am doing:
$total = (array_sum($odds))+$evens;
$total = str_split($total);
echo 'total[1]: '.$total[1].'<br />';
echo '10-$total[1]: ' . (10-($total[1]));
and the output is:
total[1]: 2
10-$total[1]: 87
my guess is it is being treated as a string, but how do i fix it?
so, what i want to know is
wh does (10-($total[1])); = 87?
Update:
yeah my mistake, a phantom 7,
but can anyone now tell me why:
echo $flybuys.' % '.$check.'<br />';
$res = $flybuys % $check;
echo 'res: '.$res;
outputs:
6014359000000928 % 8
res: 7
The inaccurate modulus result is because 6014359000000928 (~2^52) is beyond the bounds of an int, so PHP interprets it as a float. That implies you have a 32-bit system (PHP data type sizes vary depending on architecture). If you need to do math on large numbers, you can use a library like GMP. E.g.:
$flybuys = gmp_init("6014359000000928");
$res = gmp_mod($flybuys, 8);
Make sure you pass large numbers to GMP as strings.
If it is getting recognized as a string you could try casting it to an int using
(int)$total[1];
To be honest, you could probably cast the $total array into an int right when you do the string split:
(int)$total = ...;
Strings that represent numbers can also be cast into (float), and depending on which version of php you have (double).
Couldn't reproduce this issue:
$total = 2222; // some imaginary number as I don't know your $odds and $evens;
$total = str_split($total);
var_dump($total);
/*
*array(4) {
* [0]=>
* string(1) "2"
* [1]=>
* string(1) "2"
* [2]=>
* string(1) "2"
* [3]=>
* string(1) "2"
*}
*/
var_dump($total[1]);
/*
* string(1) "2"
*/
var_dump((10-($total[1])));
/*
* int(8)
*/
Absolutely the expected behavior...
I added this as an answer because in a comment is not enough space:
If this is the implementation of the algorithm described here i really think that modulo check 6014359000000928 % 8 == 0 shouldn't be there.
For example consider the number with the first 15 digits like that: 6014 3590 0000 062. For that evens is 15, odds is 24, total is 39 and check is 1. Any number modulo 1 is 0. So 6014 3590 0000 0628 is valid as 6014 3590 0000 0620 is or 6014 3590 0000 0627. That doesn't make sense.
I think you have to check the last digit for equality with check. In that case only 6014 3590 0000 0621 would be valid.
list(,$nfields) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4;
The question is, why "N*" if substr should return 4 bytes, and they will be unpacked as N? And why double assignment?
UPD: This code is part of Sphinx native PHP connector. After some code hacking it became clear that this code extracts 4-byte integer. But logic behind double assignment and substr / N* is still unclear to me. I'm offering a bounty to finally understand it.
We'd need to see the revision history of the file but some possibilities are:
These are the remains of a previous algorithm that was progressively stripped of functionality but never cleaned up.
It's the typical spaghetti code we all produce after a bad night.
It's an optimization that speeds up the code for large input strings.
These are all synonyms:
<?php
$packed = pack('N*', 100, 200, 300);
// 1
var_dump( unpack('N*', $packed) );
// 2
var_dump( unpack('N*', substr($packed, 0, 4)) );
var_dump( unpack('N*', substr($packed, 4, 4)) );
var_dump( unpack('N*', substr($packed, 8, 4)) );
// 3
var_dump( unpack('N', substr($packed, 0, 4)) );
var_dump( unpack('N', substr($packed, 4, 4)) );
var_dump( unpack('N', substr($packed, 8, 4)) );
?>
I did the typical repeat-a-thousand-times benchmark with three integers and 1 is way faster. However, a similar test with 10,000 integers shows that 1 is the slowest :-!
0.82868695259094 seconds
0.0046610832214355 seconds
0.0029149055480957 seconds
Being a full-text engine where performance is a must, I'd dare say it's an optimization.
The code is probably a bug. This kind of loop is precisely the reason why * exists...
unpack ( "N*", substr ( $response, $p,
4 ) );
Specifies the format to use when unpacking the data from substr()
N - unsigned long, always 32 bit, big endian byte order