I am having a problem with an array changing the value of keys on me. In the example I'm providing below it starts out ok, but at some point php is turning my string into an integer and changing that integer when inserting it into another array as a key.
Code
-----------------
$fund = '01';
$division = '1';
$gl_transactions[$fund] = array();
$glint = array($division=>array('gl_cash_account'=>'1800001040000000'));
print chr(10).'1:'.chr(10);
print_r($glint);
$gl = $glint[$division]['gl_cash_account'];
print chr(10).'2:'.chr(10);
print $gl;
$gl_transactions[$fund][$gl]['description'] = 'MTS DISBURSEMENTS';
print chr(10).'3:'.chr(10);
print_r($gl_transactions);
Expected output
---------------
1:Array([1] => Array([gl_cash_account] => 1800001040000000 ))
2:1800001040000000
3:Array([01] => Array([1800001040000000] => Array([description] => MTS DISBURSEMENTS)))
Actual output
---------------
1:Array([1] => Array([gl_cash_account] => 1800001040000000 ))
2:1800001040000000
3:Array([01] => Array([1721082880] => Array([description] => MTS DISBURSEMENTS)))
notice the index changed from 1800001040000000 to 1721082880
Obsticles
These big numbers are G/L Account numbers and cannot be changed to smaller numbers. We have tens of thousands of pieces of code, over a million lines of code in our product and cannot go through every bit of it to find where this may be an issue and rewrite it. This is just a generic example of something that we do in many places, building multidimensional arrays using data from a database. I can simply cast the variable as a string when inserting into the array to fix the above example, but backfilling 1M+ lines of code is not a viable option.
On my development machine I run php 5.3 MSSQL, Windows, and IIS. I do not get this error with the above code but I do get it when simply setting a variable as an integer and then inserting it into an array as a key. i.e. $gl = 1800001040000000; $ar[$gl] = 1; print_r($ar); Now we don't cast a variables to integers in our software, but in the first example php converts it on it's own at some point when it's building the last array when ran on some of our clients systems.
So my questions are:
What is the exact limit of a numeric array key.
Is there a way to increase this.
Has this been fixed or increased in later versions of php beyond 5.2
Other notes
Our software works with several databases, several browsers, window and linux, apache and IIS. We have hundreds of customers using the software all with their own unique setup. Most of our customers are on php 5.2 currently and upgrading them is not possible at this point because of deprecated functions used in our software.
My guess is that you are limited by the maximum value of an integer since PHP will automatically cast strings with valid integer values being used as array keys to integers. The PHP manual indicates the limits for integers will vary by system, but the big issue is 32-bit systems vs. 64-bit systems in determining upper limit.
http://php.net/manual/en/language.types.integer.php
The maximum integer value on a system can be read from PHP_INT_MAX constant.
I see there is no accepted answer here, and certainly the OP has moved on from this. I was losing my keys in an array like this (from Snowmed CT):
static function getNemsisProcedures() {
$nemsis[445828009] = 'Assessment Advanced Spinal Assessment (i.e., spinal clearance)';
$nemsis[425058005] = 'Assessment Orthostatic Vital Signs';
// etc....
$nemsis[89666000] = 'Cardiac CPR, Manual';
$nemsis[450661000124102] = 'Cardiac Defibrillation, AED'; // TAKE NOTE
$nemsis[426220008] = 'Cardiac Defibrillation, Manual';
// a lot more etc...
The issue was with "Cardiac Defibrillation, AED" because the key as an integer 450661000124102 was greater than PHP_MAX_INT (2147483647 in my case).
Solution: Use string keys where applicable.
In my case, I did not require the keys to be integers, and to prevent a large commit in git, I simply gave the affected Snowmed CT Procedure values keys with type string by quoting them:
static function getNemsisProcedures() {
$nemsis[445828009] = 'Assessment Advanced Spinal Assessment (i.e., spinal clearance)';
$nemsis[425058005] = 'Assessment Orthostatic Vital Signs';
// etc....
$nemsis[89666000] = 'Cardiac CPR, Manual';
// KEY IS GREATER THAN PHP_MAX_INT - USE QUOTES:
$nemsis['450661000124102'] = 'Cardiac Defibrillation, AED';
$nemsis[426220008] = 'Cardiac Defibrillation, Manual';
// a lot more etc...
Related
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.
so I'm trying to create website with a coinflip system (Its just a small project I'm doing in my free time) but I don't really know where to begin. I need to make it in php (so its in the backend) and I need it to be provably fair (so I can prove that it is legit). What I've found out is that I need to use something like sh256 but I also heard that its pretty out dated and can be easily cracked. Also if it matters it's a site with a steam login system so I plan on being able to join 1v1's with others steam users not just a person sitting besides me or something (not just 1 button is what I mean hehe).
EDIT: I have googled it and tried asking people I know and etc if they knew anything but nothing was any good.
Thanks in advance
-Eiríkur
This is a simple way to get a random coin toss result:
$result = array("heads", "tails")[random_int(0,1)];
First, we make an array, which will be our choices. array("heads, "tails") means we will always get one of those 2 results. Next, in the same line, we can select a single element to actually assign to the $result variable from the array we made previously. We can use random_int(min, max) to generate that number.
Note: random_int() generates cryptographic random integers that are
suitable for use where unbiased results are critical, such as when
shuffling a deck of cards for a poker game.
http://php.net/manual/en/function.random-int.php
As a bonus, you could add more elements to this array, and then just increase the max value in random_int(), and it will work. You could make this more dynamic as-well by doing it like this:
$choices = ["heads", "tails", "Coin flew off the table"];
$result = $choices[random_int(0, count($choices)-1];
With the above code, you can have as many choices as you'd like!
Testing
I ran this code 50,000 times, and these were my results.
Array
(
[heads] => 24923
[tails] => 25077
)
And I ran this code 100,000 times, these were my results:
Array
(
[tails] => 49960
[heads] => 50040
)
You can play around with this here, to check out results:
https://eval.in/894945
The answer above might be the best for most of the scenarios.
In commercial usage, you might want to make sure that the results can be recalculated to prove fairness.
In the following code, you need to calculate a seed for the server. Besides, you also might want to create a public seed that users can see. Those can be anything but I do recommend using some kind of a hash. Each time you get a new result just increase the round, it will generate a new truly random result.
$server_seed = "96f3ea4d221ca1b2048cc3b3b844e479f2bd9c80a870628072ee98fd1aa83cd0";
$public_seed = "460679512935";
for($round = 0;$round < 10;$round++) {
$hash = hash('sha256', $server_seed . "-" . $public_seed . "-" . $round);
if (hexdec(substr($hash, 0, 8)) % 2) {
echo 'heads', PHP_EOL;
} else {
echo 'tails', PHP_EOL;
}
}
This code will loop through 10 times using for loop, each time generating a new result. In the code, we assign a SHA256 hash to the $hash variable. Then we can calculate the decimal value from the $hash using PHP inbuilt function hexdec. We take the remainder from the decimal value and give the result based on it whether it's 0 or not.
NOTE You can play around with the values. Changing the substring to substr($hash, 0, 14) will get you a different way of generation to the results. Keep in mind that this will not change the final results in any way.
Average results of 1 000 000 runs were the following:
Heads: 50.12%
Tails: 49.88%
You can experiment with the code above at here.
By coincidence I ran into a very bizarre behaviour regarding PHP arrays and its keys. Consider this creation of an PHP array.
$arr[2250572483]=1;
//dump the array
var_dump($arr);
//Result:
array(1) { [-2044394813]=> int(1) }
Somehow the array key has changed its value to a completely different negative number. This led me to some further investigation which is still inconclusive.
In below example I loop between the number range 2250572300 and 2250572500. Time is scarces for me so I did not manage to pinpoint at what number this phenomenon starts occurring because I run out of memory looping through large range of numbers. I think it should be somewhere between 2100000000 and 4300000000.
$arr2 = array();
for($i=2250572300; $i<= 2250572500; $i++){
$arr2[$i]=$i;
}
echo "<pre>".var_export($arr2,true)."</pre>";
My question is: does anyone know how and why this is happening and is there anything that is currently being done to fix the problem?
Essentially this is a major design flaw within PHP and could potentially make PHP useless when you are working with numbers in arrays, examples being supplier, invoice, item numbers etc.
Thanks
You're using an integer value as the array key. All integers in PHP are signed integers and on 32-bit systems, the maximum value is 232 - 1 (given by PHP_INT_MAX). If the integer value is greater than PHP_INT_MAX then it wraps over and gives $key % PHP_INT_MAX as the result.
To confirm:
echo 2250572483 % PHP_INT_MAX; // => -2044394813
The solution would be to use the key as a string, i.e. $arr['2250572483']=1;. This shouldn't be a problem on 64-bit systems, though (where the upper limit is 264 - 1).
That has something to do with how integers are saved. The first bit (from the left side) say if you are positiv + or negativ - and with this behavior the result with big ints will be how you discribted it. That you have a negativ Integer.
I am using the following code in an application based on ZF1:
$select = $db->select()->from('table', array('id', 'int', 'float'))->limit(10000, (($i - 1) * 10000));
$data = $select->query();
while ($row = $data->fetch()) {
# ...
}
This operation is happening in a foreach loop for some 800 times. I output the memory usage for each pass and can see it increasing by about 5MB per pass. I suppose that is because Zend apparently does not free the result from the query once the pass is complete. A simple unset didn't solve the issue. Using fetchAll also did not improve (or change) the situation.
Is there any way to free the result from a Zend_Db_Statement_PDO thus freeing the memory used by it? Or do you suspect another reason?
I believe you want to do this:
$sql = "SELECT something FROM random-table-with-an-obscene-large-amount-of-entries";
$res = $db->query($sql);
while ($row = $res->fetch(Zend_Db::FETCH_NUM)) {
// do some with the data returned in $row
}
Zend_Db::FETCH_NUM - return data in an array of arrays. The arrays are indexed by integers, corresponding to the position of the respective field in the select-list of the query.
Since you overwrite $row on each loop, the memory should be reclaimed. If you are paranoid you can unset($row) at the bottom of the loop I believe. I've not tested this myself recently, but I ran into a batch problem about a year ago that was similar, and I seem to recall using this solution.
Actually the problem was hidden somewhere else:
Inside the loop some integer results were stored in an array for modification at a later planned stage in the workflow.
While one might expect PHP arrays to be small, that is not the case: Arrays grow big really fast and a PHP array is on average 18 times larger than it is to be 'expected'. Watch out while working with arrays, even if you only store integers in them!
In case the linked article disappears sometime:
In this post I want to investigate the memory usage of PHP arrays (and values in general) using the following script as an example, which creates 100000 unique integer array elements and measures the resulting memory usage:
$startMemory = memory_get_usage();
$array = range(1, 100000);
echo memory_get_usage() - $startMemory, ' bytes';
How much would you expect it to be? Simple, one integer is 8 bytes (on a 64 bit unix machine and using the long type) and you got 100000 integers, so you obviously will need 800000 bytes. That’s something like 0.76 MBs.
Now try and run the above code. This gives me 14649024 bytes. Yes, you heard right, that’s 13.97 MB - eightteen times more than we estimated.
I want to build a chessboard via bitboard system.
Starting with 12 bitboards i want to display a table (chessboard), during loop/iteration a piece must be drawn.
How do i loop through all bitvalues?
I was thinking of something like:
for(i=0;i<64;i++)
draw table / build array / draw empty square
These are my my values to start a game:
function init_game($whitePlayer,$blackPlayer)
{
$WhitePawns = '0000000000000000000000000000000000000000000000001111111100000000';
$WhiteKnights = '0000000000000000000000000000000000000000000000000000000001000010';
$WhiteBishops = '0000000000000000000000000000000000000000000000000000000000100100';
$WhiteRooks = '0000000000000000000000000000000000000000000000000000000010000001';
$WhiteQueens = '0000000000000000000000000000000000000000000000000000000000010000';
$WhiteKing = '0000000000000000000000000000000000000000000000000000000000001000';
$BlackPawns = '0000000011111111000000000000000000000000000000000000000000000000';
$BlackKnights = '0100001000000000000000000000000000000000000000000000000001000010';
$BlackBishops = '0010010000000000000000000000000000000000000000000000000000100100';
$BlackRooks = '1000000100000000000000000000000000000000000000000000000000000000';
$BlackQueens = '0000100000000000000000000000000000000000000000000000000000000000';
$BlackKing = '0001000000000000000000000000000000000000000000000000000000000000';
$WhitePieces = $WhitePawns|$WhiteKnights|$WhiteBishops|$WhiteRooks|$WhiteQueens|$WhiteKing;
$BlackPieces = $BlackPawns|$BlackKnights|$BlackBishops|$BlackRooks|$BlackQueens|$BlackKing;
}
Some people asked me: why bitboard appoach?
Answer:
About bitboard
A bitboard, often used for boardgames such as chess, checkers and othello, is a specialization of the bitset data structure, where each bit represents a game position or state, designed for optimization of speed and/or memory or disk use in mass calculations. Bits in the same bitboard relate to each other in the rules of the game often forming a game position when taken together. Other bitboards are commonly used as masks to transform or answer queries about positions. The "game" may be any game-like system where information is tightly packed in a structured form with "rules" affecting how the individual units or pieces relate.
First you have to check if your PHP version supports 64bit integers, otherwise you will have strange results.
Just run:
echo PHP_INT_MAX;
and if result is 9223372036854775807 then it should work.
You're using strings and I suppose that when you'll do $string | $string in form like you're doing it above then it will be cast as integer with base 10, so the result won't be what you want. Since PHP 5.4 you can use 0b000 notation, for lower PHP version you'll need to keep it in hexadecimal or base 10 format. If you're storing values in DB or somewhere like that and you'll receive value as string or you just want to keep it in format presented above, then you have to use intVal($value, 2) first to cast it properly.
To iterate over the value you can use just for loop (as you suggested):
$value = intVal($WhitePieces,2);
for ($i = 0 ; $i < 64 ; ++$i) {
if ((pow(2,$i) & $value)) {
// draw piece
}
}
You do not have bitvalues, you do have strings. And strings should be difficult to or.
How do you loop? Use an array and foreach.
How do you use 64bit values? Use PHP 5.4 and the binary number format: 0b00001111 => 16 - alternatively express the integer value as hex or decimal, which should be completely ok for a game setup routine that will not change because the rules are known for centuries.
Remember that you have to use a 64Bit system to execute your code, otherwise PHP will be unable to support 64Bit integers, and either treat them as float values, or shorten them to 32Bit values, depending on what you actually do.
Because of all this, I'd suggest NOT to use bit fields for the solution. They seem like a great idea to program more assembler-like, but you are not writing assembler, and will probably pay for this approach with non-optimal performance compared to anything else.