I'm having trouble calculating roots of rather large numbers using bc_math, example:
- pow(2, 2) // 4, power correct
- pow(4, 0.5) // 2, square root correct
- bcpow(2, 2) // 4, power correct
- bcpow(4, 0.5) // 1, square root INCORRECT
Does anybody knows how I can circumvent this? gmp_pow() also doesn't work.
I'm not a PHP programmer but looking at the manual it says you have to pass them in as strings i.e.
bcpow( '4', '0.5' )
Does that help?
Edit: The user contributed notes in the manual page confirm that it doesn't support non-integer exponents.
I did come across this discussion of a PHP N-th root algorithm after a quick search so perhaps that's what you require.
Related
I'm trying to format a given number into a simple pattern in PHP, used for a vehicle Engine Size display.
The pattern needs to be [digit][dot][digit] - i.e. 1.8 or 2.0 or 0.7
I have zero control over validation of the data that is entered. I can only use what is given. However, it could follow any of these patterns:
1, 1.2, 1600, 1998.9, 1920
Given the examples above, here is what I would need to return:
1 = 1.0
1.2 = 1.2
1600 = 1.6
1998.9 = 2.0
1920 = 1.9
I've tried playing with round(), floor() and number_format() but I can't quite figure out a way to 'catch-all' given possibilities.
I could probably sit down and write a huge function to get this working, but I'm not too knowledgeable on optimising my PHP - so just wondering if there's a graceful solution that could exist?
We can abuse/exploit PHP's loose typing for this, then with some Regex "magic" a pinch of number formatting and 67bytes - ad {waves hands} vola'
$inputs = [1, 1.2, 1600, 1998.9, 1920];
foreach($inputs as $input)
echo number_format(preg_replace(['/\./','/^(\d)/'],['','\1.'],$input),1)."\n";
Output
1.0
1.2
1.6
2.0
1.9
Sandbox
Basically what I am doing is
'/\./' replace . with ''
remove dots
'/^(\d)/' capture the first digit (group 1)
replace this match with itself (the first digit) and a dot \1.
Format it with 1 decimal place
Basically it places the decimal after the first number (no matter where it was), then rounds it to 1 place. So for 1998.9
remove dots 19989
add dot after first digit 1.9989
format 2.0
Because PHP is loosely typed it doesn't care if its a number like string or a number. So we can just shift the decimal were we want it and PHP could care less ... lol ... if it looks like a number, and smells like a number it must be a number.
P.S. There are of course many ways to do the above, but this preg_replace was the first one I thought of that I could do in 1 line.
Cheers!
UPDATE
Here is one (golfed a bit) that doesn't use Regex
$inputs = [1, 1.2, 1600, 1998.9, 1920];
foreach($inputs as $input)
echo number_format((($i=str_replace(".","",$input))?substr($i,0,1).".".substr($i,1):$i),1)."\n";
Sandbox
I'm sure someone with better math skills can come up with a cleaner solution than using a while loop, but this does what you ask for:
function formatNumber($num)
{
// We divide the number until it's less than 10
while ($num >= 10) {
$num = $num / 10;
}
// Now we round the number to one decimal precision.
return round($num, 1);
}
Here's a demo
In PHP, I am writing an application which requires precision to 2 digits right of the decimal point for currency (eg: I care about 1.23 === 1.23 but no more right-side digits).
I am aware that floats are generally considered bad practice because they are imprecise with values based on the nature of converting from base 2 to base 10 right of the decimal point. However, in my research for a best practice for working with currency values, I saw some arguments that float is not good if you need precision greater than whole cent values. I clearly do not need greater precision that whole cent values.
So my questions, then, are:
Is it worth going through the extra effort of storing the values as strings to be used with the bcmath library?
If using the bcmath lib, should I store the values in the MySQL db as strings or decimal that MySQL supports?
Thanks!
After further digging, I found the solution at Should I use BCMath for values with about 1,2 or 3 decimals?
According the the accepted answer on the given post, floats can not be guaranteed for any precision right of the decimal point.
As far as storage in the DB, it seems that storing it as a string would be the easiest option since the bcmath lib works with strings.
Use this to trim to two decimal places without rounding.
<?php
$a = 12.37675;
$a = floor($a * 100) / 100; // 12.37
echo $a;
or
<?php
function dollar($value) {
return floor($value * 100) / 100;
}
$a = 12.37675;
echo dollar($a);
I've been wrestling with PHP's ceil() function giving me slightly wrong results - consider the following:
$num = 2.7*3; //float(8.1)
$num*=10; //float(81)
$num = ceil($num); //82, but shouldn't this be 81??
$num/=10; //float(8.2)
I have a number which may have any number of decimal places, and I need it rounded up to one decimal place.
i.e 8.1 should be 8.1, 8.154 should be 8.2, and 8 should be left as 8.
How I've been getting there is to take the number, multiply by 10, ceil() it, then divide by ten but as you can see I'm getting an extra .1 added in some circumstances.
Can anyone tell my why this is happening, and how to fix it?
Any help greatly appreciated
EDIT: had +=10 instead of *=10 :S
EDIT 2:
I didn't explicitly mention this but I need the decimal to ALWAYS round UP, never down - this answer is closest so far:
rtrim(rtrim(sprintf('%.1f', $num), '0'), '.');
However rounds 3.84 down to 3.8 when I need 3.9.
Sorry this wasn't clearer :(
Final Edit:
What I ended up doing was this:
$num = 2.7*3; //float(8.1)
$num*=10; //float(81)
$num = ceil(round($num, 2)); //81 :)
$num/=10; //float(8.1)
Which works :)
This is more than likely due to floating point error.
http://support.microsoft.com/kb/42980
http://download.oracle.com/docs/cd/E19957-01/806-3568/ncg_goldberg.html
http://joshblog.net/2007/01/30/flash-floating-point-number-errors/
http://en.wikipedia.org/wiki/Floating_point
You may have luck trying this procedure instead.
<?php
$num = 2.7*3;
echo rtrim(rtrim(sprintf('%.1f', $num), '0'), '.');
Floats can be a fickle thing. Not all real numbers can be properly represented in a finite number of binary bits.
As it turns out, a decimal section of 0.7 is one of those numbers (comes out 0.10 with an infinity repeating "1100" after it). You end up with a number that's ever so slightly above 0.7, so when you multiply by 10, you have a one's digit slightly above 7.
What you can do is make a sanity check. Take you float digit and subtract it's integer form. If the resulting value is less than, say, 0.0001, consider it to be an internal rounding error and leave it as-is. If the result is greater than 0.0001, apply ceil() normally.
Edit: A fun example you can do if you're on windows to show this is to open up the built in calculator application. Put in "4" then apply a square root function (with x^y where y=0.5). You'll see it properly displays "2". Now, subtract 2 from it and you'll see that you don't have 0 as a result. This is caused by internal rounding errors when it attempted to compute the square root of 4. When displaying the number 2 earlier, it knew that those very distant trailing digits were probably a rounding error, but when those are all that's left, it gets a bit confused.
(Before anybody gets onto me about this, I understand that this is oversimplified, but nonetheless I consider it a decent example.)
Convert your number to a string and ceil the string.
function roundUp($number, $decimalPlaces){
$multi = pow(10, $decimalPlaces);
$nrAsStr = ($number * $multi) . "";
return ceil($nrAsStr) / $multi;
}
The problem is that floating point numbers are RARELY what you expect them to be. Your 2.7*3 is probably coming out to be something like 81.0000000000000000001, which ceil()'s up to 82. For this sort of thing, you'll have to wrap your ceil/round/floor calls with some precision checks, to handle those extra microscopic differences.
Use %f instead of %.1f.
echo rtrim(rtrim(sprintf('%f', $num), '0'), '.');
Why not try this:
$num = 2.7*3;
$num *= 100;
$num = floor($num);
$num /= 10;
$num = ceil($num);
$num /= 10;
In PHP I have a 64 bit number which represents tasks that must be completed. A second 64 bit number represents the tasks which have been completed:
$pack_code = 1001111100100000000000000011111101001111100100000000000000011111
$veri_code = 0000000000000000000000000001110000000000000000000000000000111110
I need to compare the two and provide a percentage of tasks completed figure. I could loop through both and find how many bits are set, but I don't know if this is the fastest way?
Assuming that these are actually strings, perhaps something like:
$pack_code = '1001111100100000000000000011111101001111100100000000000000011111';
$veri_code = '0000000000000000000000000001110000000000000000000000000000111110';
$matches = array_intersect_assoc(str_split($pack_code),str_split($veri_code));
$finished_matches = array_intersect($matches,array(1));
$percentage = (count($finished_matches) / 64) * 100
Because you're getting the numbers as hex strings instead of ones and zeros, you'll need to do a bit of extra work.
PHP does not reliably support numbers over 32 bits as integers. 64-bit support requires being compiled and running on a 64-bit machine. This means that attempts to represent a 64-bit integer may fail depending on your environment. For this reason, it will be important to ensure that PHP only ever deals with these numbers as strings. This won't be hard, as hex strings coming out of the database will be, well, strings, not ints.
There are a few options here. The first would be using the GMP extension's gmp_xor function, which performs a bitwise-XOR operation on two numbers. The resulting number will have bits turned on when the two numbers have opposing bits in that location, and off when the two numbers have identical bits in that location. Then it's just a matter of counting the bits to get the remaining task count.
Another option would be transforming the number-as-a-string into a string of ones and zeros, as you've represented in your question. If you have GMP, you can use gmp_init to read it as a base-16 number, and use gmp_strval to return it as a base-2 number.
If you don't have GMP, this function provided in another answer (scroll to "Step 2") can accurately transform a string-as-number into anything between base-2 and 36. It will be slower than using GMP.
In both of these cases, you'd end up with a string of ones and zeros and can use code like that posted by #Mark Baker to get the difference.
Optimization in this case is not worth of considering. I'm 100% sure that you don't really care whether your scrip will be generated 0.00000014 sec. faster, am I right?
Just loop through each bit of that number, compare it with another and you're done.
Remember words of Donald Knuth:
We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil.
This code utilizes the GNU Multi Precision library, which is supported by PHP, and since it is implemented in C, should be fast enough, and supports arbitrary precision.
$pack_code = gmp_init("1001111100100000000000000011111101001111100100000000000000011111", 2);
$veri_code = gmp_init("0000000000000000000000000001110000000000000000000000000000111110", 2);
$number_of_different_bits = gmp_popcount(gmp_xor($pack_code, $veri_code));
$a = 11111;
echo sprintf('%032b',$a)."\n";
$b = 12345;
echo sprintf('%032b',$b)."\n";
$c = $a & $b;
echo sprintf('%032b',$c)."\n";
$n=0;
while($c)
{
$n += $c & 1;
$c = $c >> 1;
}
echo $n."\n";
Output:
00000000000000000010101101100111
00000000000000000011000000111001
00000000000000000010000000100001
3
Given your PHP-setuo can handle 64bit, this can be easily extended.
If not you can sidestep this restriction using GNU Multiple Precision
You could also split up the HEx-Representation and then operate on those coresponding parts parts instead. As you need just the local fact of 1 or 0 and not which number actually is represented! I think that would solve your problem best.
For example:
0xF1A35C and 0xD546C1
you just compare the binary version of F and D, 1 and 5, A and 4, ...
How can I parse a configuration value with PHP which is a number and a total of other numbers.
One example of this is:
1 -> Logging Enabled
2 -> Error Reporting Enabled
4 -> E-Mail Reporting Enabled
3 -> Logging + Error Enabled
5 -> Logging + E-Mail Enabled
You don't just have a sum -- you have yourself a set of flags, or a bit field, with each flag represented by one bit.
$logging = !!($cfgval & 1);
$errorReport = !!($cfgval & 2);
$emailReport = !!($cfgval & 4);
The "!!" just ensures that numbers that aren't 0 (ie: numbers with the specific bit set) end up as the same "true" value that the rest of PHP uses, so stuff like ($logging == true) always works as expected. It's not required, but i highly recommend you convert the value to a boolean somehow; (bool) would work as well, even if it is 3 times as many characters. :)
As long as you keep the numbers as powers of two (1, 2, 4, 8, 16, 32...), it's easy to extend this up to 31-32 different flags (integers are 32 bits in size, but the top bit is a sign bit which acts kinda funny if you don't know about "two's complement" math).