PHP equivalent for Excel's COMBIN(N,K) function? - php

I am aware that the function may be computed manually using factorials, the problem is that larger numbers will not calculate properly.
For example, if I input COMBIN(1500,5) in MS Excel it will return 62,860,358,437,800 as it should. But if I try to calculate it manually, even in Excel I will get a #NUM! error when I try to first find the factorial of 1500. The manual formula would be
1500!/5!(1500-5)!
I find it curious that Excel's COMBIN function calculates properly yet the manual way returns an error. In short, I am wondering if there is an equivalent of this function in PHP? I have already tried manually computing using gmp_fact and as with Excel, it returns error (NaN).
Thanks

Your calculation is failing because you're quickly overflowing the Integer data type, which is probably just 32-bits on your system. You could use the arbitrary precision math functions to get around that problem:
http://www.php.net/manual/en/ref.gmp.php
If gmp_fact(...) seems to be returning errors or bad results, you're probably passing it a bad value or assuming it's result is a basic numeric type. You'll want to use something like gmp_strval to convert the returned GMP resource into something readable when you're done performing calculations.
Example:
$a = gmp_fact(1500);
$b = gmp_fact(5);
$c = gmp_fact(1500-5);
$result = gmp_div($a,(gmp_mul($b,$c)));
echo gmp_strval($result);

Related

PHP round() function does not round the number

It looks like PHP round() does not behave as expected. Look at the following code:
log_rec("BEFORE ROUNDING","x", $x);
$x = round($x, 3);
log_rec("AFTER ROUNDING","x", $x);
where log_rec() is simply a function of mine to write a record in a log file,
and $x value is initially set to 3.339999999999999857891452847979962825775146484375.
I expect $x to be 3.34 after rounding.
Here is the result in log file:
▶"BEFORE ROUNDING"■"x"■3.339999999999999857891452847979962825775146484375■
▶"AFTER ROUNDING"■"x"■3.339999999999999857891452847979962825775146484375■
No change. What am I doing wrong? I am using PHP 7.4.26.
NOTE: I need to round the number, that is, to obtain another float number. So, number_format() is not a solution.
UPDATE
I decided to use a different approach, that is to use
log_rec("BEFORE ROUNDING","x", $x);
$x= intval(100 * $x)/100;
log_rec("AFTER ROUNDING","x", $x);
I expected now
▶"BEFORE ROUNDING"■"x"■3.339999999999999857891452847979962825775146484375■
▶"AFTER ROUNDING"■"x"■3.34■
but I still get
▶"BEFORE ROUNDING"■"x"■3.339999999999999857891452847979962825775146484375■
▶"AFTER ROUNDING"■"x"■3.339999999999999857891452847979962825775146484375■
Now I am totally confused. This is a sequence of three well defined instructions. No other code should create some collateral effect.
Any idea?
PS: WHERE THE NUMBER COMES FROM?
Someone asked where that big number comes from.
From a JSON file. What is strange is that that number in JSON is 3.334, but after json_decode() something strange happens: if I use var_dump() I get 3.334 but if I use print_r() I get that big number.

PHP saving precision float into MYSQL ends up with rounded resulting field

I've read all over about arithmetic regarding floating point numbers, but I'm just trying to accurately store the darn things.
I have a mysql field with the type of DECIMAL (40,20).
I am saving a value in php of 46457.67469999996. After updating the record with this value, the end result is 46457.67470000000000000000. Not sure why it's being rounded at all just being saved to the database.
The value is not being converted to a string or anything beforehand. The field value that is passed into PDO is the value I expected to be saved and it is returned as a float... Perhaps it's because I'm saving a PHP float to a mysql decimal type where the rounding is occurring?
What am I missing here?
EDIT: Added example code that has the issue
// Query placeholder variables. Hard-coded for the example test
$query_vars = array(
":vendor_id" => 33154,
":year" => 2018,
":coop_committed_dollar" => 46457.67469999996,
":coop_committed_dollar_update" => 46457.67469999996
);
$statement = " INSERT INTO vendor_data_yearly
(vendor_id, year, coop_committed_dollar) VALUES
(:vendor_id, :year, :coop_committed_dollar)
ON DUPLICATE KEY UPDATE
coop_committed_dollar = :coop_committed_dollar_update;";
$query = $connection->conn->prepare($statement);
$query->execute($query_vars);
When I run this, the resulting value of coop_committed_dollar is 46457.67470000000000000000. This code is legit all I am doing.
Possible solution
// Note that I am casting the string using the BC Math library.
// I dunno how to just initialize the number (lame documentation), so I'm adding 0 to it.
$number = "46457.674699999967";
$number = bcadd("46457.674699999967", 0, 20);
$query_vars = array(
":vendor_id" => 33154,
":year" => 2018,
":coop_committed_dollar" => $number,
":coop_committed_dollar_update" => $number
);
$statement = " INSERT INTO vendor_data_yearly
(vendor_id, year, coop_committed_dollar) VALUES
(:vendor_id, :year, :coop_committed_dollar)
ON DUPLICATE KEY UPDATE
coop_committed_dollar = :coop_committed_dollar_update;";
$query = $conn->prepare($statement);
$query->execute($query_vars);
This results in the number as expected in the DB.
ONLY SOLUTION I FOUND TO WORK CORRECTLY
The data I am working with is passed in via ajax. I had to take a few steps to get this to work correctly.
Use ini_set('precision', 20);
Manually set the data in question to be a string BEFORE sending it via ajax so PHP would not round it, extended with extra floating point madness, padd it, etc.
I found that PHP would just not let me reliably work with large numbers coming from a variable set outside the script's scope (ajax). Once PHP got it's hands on the number, it would do what it had to do in order to make it make sense as a float.
If anyone has a better solution for this particular scenario I'm all ears and eyes :)
The problem is that PHP's precision is not allowing you to store the exact number you think you are storing.
When you set ":coop_committed_dollar" => 46457.67469999996
PHP is actually storing it as a different value, depending on the precision.
The solution is to store the value in PHP as a string instead of a float.
Since your question is: "what am I missing", I will try to provide an answer.
Basically it comes down to storing floats internally using binary representation. Since 46457.67469999996 cannot be exactly in binary (it ends up with an infinite number, similar to 33% (.3333...) in base-10), the closest rounding is used based on PHP's precision (set in php ini).
I was given a great explanation in this question that I asked a while back.
In your particular case, it also seems that the value that you are sending via AJAX is being stored as a float when parsed by PHP on the server-side. You want it to be stored as a string instead. If you're using json_decode, add this option: JSON_BIGINT_AS_STRING.

Why is bcdiv always returning 0?

I have a small issue with BIG numbers where BC Maths function bcdiv is always returning zero on non-zero results.
For example :
echo bcdiv(40075036, 86164.098903691, 40);
Versus the traditional method :
echo (40075036/86164.098903691);
I am not sure why the discrepency. Do BC Math functions only work on strings, and if so, how can i convert int values into strings before hand (inline notation preferred such as (int)$myvar; ) --- if that is the problem.
The solution to this problem was as follows:
bcmath operations only work with strings. You can not pass any other type of data as the parameters as it will not do any calculations unless the values are (string).
The set of functions will not automatically convert or cast the data into string, and NO ERROR WILL BE RETURNED if passing other data types.
To pass the data that is stored in an integer, float, etc, the following code will work for dynamic conversion of the variable data as needed:
bcdiv((string)40075036, (string)86164.098903691, (string)40);
OR
$num1 = 12345;
$num2 = 45678;
$digits = 40;
bcdiv((string)$num1, (string)$num2, (string)$digits);
If the numbers are fixed and known BEFORE you pass (aka, they aren't coming from a database, and aren't pre-calculated), then you should use the function as follows :
bcdiv('40075036', '86164.098903691', '40');
Use the "bcscale" function - Set default scale parameter for all bc math functions.
Ex.:
bcscale(40);
echo bcdiv(40075036, 86164.098903691);
It worked for me. (php 7.1)
bcscale manual

Php rand vs Perl rand

I am trying to port a piece of code from perl to php. The perl code snippet is part of akamai's video on demand link generation script. The script generates seed based on the location / URL of the video file (which will always be constant for a single URL). And then it is used in generating serial ID for stream (which is basically a random number between 1 and 2000 using the seed). Here is the perl code.$seed=6718;
srand($seed);
print(int(rand(1999)) + 1); // return 442 every time And the converted PHP code is:$seed=6718;
srand($seed);
echo(rand(0, 1999) + 1); //returns 155 every time
Does php rand behaves differently than perl one?
Yes. You can't depend on their algorithms being the same. For perl, which rand is used depends on what platform your perl was built for.
You may have more luck using a particular algorithm; for instance, Mersenne Twister looks to be available for both PHP and Perl.
Update: trying it produces different results, so that one at least won't do the trick.
Update 2: From the perl numbers you show, your perl is using the drand48 library; I don't know whether that's available for PHP at all, and google isn't helping.
[clippy]It looks like your trying to hash a number, maybe you want to use a hash function?[/clippy]
Hash functions are designed to take an input and produce a consistently repeatable value, that is in appearance random. As a bonus they often have cross language implementations.
Using srand() with rand() to get what is basically a hash value is a fairly bad idea. Different languages use different algorithms, some just use system libraries. Changing (or upgrading) the OS, standard C library, or language can result in wildly different results.
Using SHA1 to get a number between 1 and 2000 is a bit overkill, but you can at least be sure that you could port the code to nearly any language and still get the same result.
use Digest::SHA1;
# get a integer hash value from $in between $min (inclusive) and $max (exclusive)
sub get_int_hash {
my ($in, $min, $max) = #_;
# calculate the SHA1 of $in, note $in is converted to a string.
my $sha = Digest::SHA1->new;
$sha->add( "$in" );
my $digest = $sha->hexdigest;
# use the last 7 characters of the digest (28 bits) for an effective range of 0 - 268,435,455.
my $value = hex substr $digest, -7;
# scale and shift the value to the desired range.
my $out = int( $value / 0x10000000 * ( $max - $min ) ) + $min;
return $out;
}
print get_int_hash(6718, 1, 2000); #this should print 812 for any SHA1 implementation.
Just seeing this snippet of code it is impossible to say if it is the same.
At first you need to knew that even a random generator like the rand() function is not really random. It calculates a new value with a mathematical formula from the previous number. With the srand() function you can set the start value.
Calling srand() with the same argument each time means that the program always returns the same numbers in the same order.
If you really want random numbers, in Perl you should remove the initialization of srand(). Because Perl automatically sets srand() to a better (random) value when you first call the rand() function.
If your program really wants random numbers, then it should also be okay for PHP. But even in PHP i would look if srand() is automatically set and set to a more random value.
If your program don't work with random numbers and instead really want a stream of numbers that is always the same, then the snipet of code are probably not identical. Even if you do the same initialization with srand() it could be that PHP uses another formula to calculate the next "random" number.
So you need to look at your surrounding code if you code really wants random numbers, if yes you can use this code. But even then you should look for a better initialization for srand().

PHP Ignoring Digits to the Right of the Decimal Point in Equation

I have a variable $x whose value is read in from an XML file. The value being read from the XML is 1.963788, nothing more, nothing less. When I output $x, I see that the value in $x is in fact 1.963788. All is right with the world.
But then when I use x in an equation such as
$pl = $x*125.0-200.0;
The value of $pl ends up being -75. For whatever reason, PHP seems to be ignoring, or just getting rid of, the digits to the right of the decimal point in $x, which makes $x contain 1. I thought maybe there was a snowball's chance in hell that this occurred in other languages too, so I wrote it up in C++ and, big surprise, I get the right answer of 45.4735.
Anyone ever encountered this before or know what's going on? Thanks.
Have you tried using floatval?
Maybe PHP interprets your number as a string and the standard conversion just casts it to integer.
It probably is due to the fact that $x is being interpreted as a string, and converted to an integer and not a float value.
Try:
$pl = (float) $x * 125.0 - 200.0;
Your number appears to have failed casting as a float. If I use '1,963788' I get your result. If I use '2,963788' I receive a result of 50. According to the PHP docs for intval (and that's what it appears PHP is trying to cast this as, an integer):
Strings will most likely return 0 although this depends on the leftmost characters of the string. The common rules of integer casting apply.
Check the value $x actually has carefully. It may not be what you expect since PHP seems to disagree that it is, in fact, a float or it would have typed it as such.
Just before you compute $pl, do a var_dump on $x to see what is the actual value stored in it. I've tried your code and it is returning the correct value 45.4735, so I might not be PHP's fault.

Categories