Convert array of values into a single float value in PHP? - php

I have an array with these values (when the array is printed with print_r();
Array:
[0] => 66
[1] => 233
[2] => 204
[3] => 205
The values in hex are:
Array:
[0] => 0x42
[1] => 0xE9
[2] => 0xCC
[3] => 0xCD
What I'm looking to do is to turn this 4 byte array into a float value. If I use implode(); to turn the array into a value, it just combines the string into 66233204205 instead of 0x42E9CCCD which are not similar. Thus I can't use floatval(). PHP is new to me, and so is using string values instead of the actual bits, like I can in C.
What I'm thinking is to some how implode() it with the hex values, instead of those integer numbers, and then use floatval().
Any ideas guys?
EDIT:
Just so it's a little clearer, I should be obtaining 116.900 as the result

You have to do a simple math operation to concatenate hex values of the array one after the other. The algorithm would be like this:
Assign the first hex value of the array to a resultant variable, $concat in this case.
Use a for loop to loop through the array from 2nd element till nth element
In each iteration of the loop left shift 8 times the existing hex value of the resultant variable and place the new hex value in the least significant 8 bits of the resultant variable.
// Suppose $array is your original array
$concat = $array[0];
$count = count($array);
for($i = 1; $i < $count; $i++){
$concat = ($concat << 8) + $array[$i];
}
// display concatenated hex value: 42e9cccd
var_dump(dechex($concat));
// Now do your operation on the concatenated hex value
Here's a demo, https://eval.in/844793

Revised Answer with ....
Performing math with hex strings used to be a feature supported in PHP. Now with PHP 7, a hex string only represents a string of characters and no longer is recognized as containing a numeric value. If you attempt to do math with it, the result is zero. Consider the following code:
<?php
$arr = [66, 233, 204, 205];
$res = array_reduce( $arr, function($c,$i) {
$c.=dechex( $i );
return $c;
});
$temp = "0x" . $res; // 0x42e9cccd
var_dump($temp + 0);
See demo
This code attempts to provide the hex string a mathematical context by adding zero to the value contained in $temp. This code works until PHP 7 because the powers that be determined that hexstrings created more problems than they were worth; see this RFC and the Manual:"Hexadecimal strings are no longer considered numeric".
Concatenation, being a string operation, creates the example's hex string whose direct usage proves unwise in a math operation. A notice will be emitted (in PHP 7.1), complaining as follows:
Notice: A non well formed numeric value encountered
You may suppress displaying this notice, but the resulting sum will be zero in PHP 7. When the code functions correctly in PHP 5.6, the result of 1122618573 seems wrong, certainly far too large to cast as a float and obtain the value that the OP seeks.
... A Bona Fide Work-Around
<?php
$arr = [66, 233, 204, 205];
$res = array_reduce( $arr, function($c,$i) {
$c.=dechex( $i );
return $c;
});
$temp = "0x" . $res;
$int = filter_var( $temp, FILTER_VALIDATE_INT, FILTER_FLAG_ALLOW_HEX );
if (false === $int) {
throw new Exception("Invalid integer!");
}
$arr = (unpack('f', pack('i', $int )));
$f = array_pop($arr);
printf("%.3f",$f);
See demo
PHP will recognize the numeric hex string that array_reduce() yields if you use filter_var() with the indicated parameters. In this fashion, you may obtain an integer evaluating as 1122618573. The key thing rather than the integer's value is its binary bit pattern. Borrowing from the official answer here, the code needs to pack $int into a binary string, which it subsequently will unpack as a float -- almost. That result will be returned as an array with just one element. After popping off and capturing that element's float value, printf() displays 116.900.

This appends the values of the array to eachother (in hexadecimal). PHP's dechex() function.
http://php.net/dechex
dechex — Decimal to hexadecimal
$b = [66,233,204,205];
$a = dechex($b[0]);
for($x = 1; $x < count($b); $x++) {
$a = $a . dechex($b[$x]);
}
echo $a; // $a = 42e9cccd

You didn't specify if your array represents an integer, if is the integer part of the floating point value, or is the entire number represented in IEEE 754 format.
Anyway, I would suggest you to take a look at the "pack" function.
$value = pack('i', your_value);
HERE you can find the documentation: basically you have to provide the type you want to obtain, along with your value(s), of course.
Also PHP is NOT a strongly typed language, so you don't have to distinguish integer from floats, in this case. You can treat integer like floats, and viceversa. But if you want to be 100% sure, just do something like this:
$value = floatval(pack('i', your_value));
This is, of course, machine dependent, but I don't know of any machine running PHP that doesn't use IEEE 754 floats.

Related

strlen on array returns 5 in php

From the docs -
int strlen ( string $string )
it takes string as a parameter, now when I am doing this-
$a = array('tex','ben');
echo strlen($a);
Output -
5
However I was expecting, two type of output-
If it is an array, php might convert it into string so the array will become-
'texben' so it may output - 6
If 1st one is not it will convert it something like this -
"array('tex','ben')" so the expected output should be - 18 (count of all items)
But every time it output- 5
My consideration from the output is 5 from array word count but I am not sure. If it is the case how PHP is doing this ?(means counting 5)
The function casts the input as a string, and so arrays become Array, which is why you get a count of 5.
It's the same as doing:
$a = array('tex','ben');
echo (string)$a; // Array
var_dump((string)$a); // string(5) "Array"
This is the behavior prior to PHP 5.3. However in PHP 5.3 and above, strlen() will return NULL for arrays.
From the Manual:
strlen() returns NULL when executed on arrays, and an E_WARNING level error is emitted.
Prior [to 5.3.0] versions treated arrays as the string Array, thus returning a string length of 5 and emitting an E_NOTICE level error.
Use
$a = array('tex','ben');
$lengths = array_map('strlen',$a);
to get an array of individual lengths, or
$a = array('tex','ben');
$totalLength = array_sum(array_map('strlen',$a));
to get the total of all lengths
The array is implicitly converted to a string. In PHP this yields the output Array, which has 5 letters as strlen() told you.
You can easily verify this, by running this code:
$a = array('tex','ben');
echo $a;

What is this bitwise OR doing in this weird array key construct?

Can someone explain to me what this means?? I have never seen this construct - taken from the Prestashop doc
foreach ( $languages as $language )
{
echo '<div id="test_' . $language['id_lang'|'id_lang'] .... // <-- What the??
// ...
}
$language contains the following keys:
Array
(
[id_lang] => 1
[name] => English (English)
// and others...
)
The result is that it takes the value of $language["id_lang"] - 1. But I don't understand the syntax and can't find any documentation about it.
This php -a session shows that it's totally meaningless:
php > $value = 'something'|'something';
php > echo $value;
something
php > $arr = array('abc' => 1, 'def' => 2);
php > echo $arr['abc'|'abc'];
1
php > echo $arr['def'|'def'];
2
Basically, if you "bitwise or" anything by itself, you get the original value. This property is called idempotence in mathematics. For further info, read:
http://en.wikipedia.org/wiki/Idempotence
http://en.wikipedia.org/wiki/Bitwise_operation#OR
Honestly, the original author of that code had no idea what they were doing.
What that does is use the bitwise operator on the ASCII values of the characters in the string "id_lang", although why they are doing this is beyond me, since the result is always going to be the same.
To elaborate a little bit, let's say (for convenience) that we're using ASCII, where each character is encoded as a single byte. Let's look at what happens when it does the comparison for the binary representation of the first character (i is 105, which in binary is 01101001):
"i": 01101001
OR "i": 01101001
___________
= 01101001
= "i"
0|0 is 0, 1|1 is 1, so inevitably all bits remain unchanged.
It's not doing anything, strangely enough.
var_dump('id_lang'|'id_lang');
#=> string(7) "id_lang"
http://ideone.com/zXdRMO
Even if it was doing something, using a bitwise operator on a string-based array key certainly feels like code smell to me.

How to sort an array of Roman numerals?

I have an array containing Roman numerals (as strings of course). Like this:
$a = array('XIX', 'LII', 'V', 'MCCXCIV', 'III', 'XIII');
I'd like to sort them according to the numeric values of these numerals, so the results should be something like:
$sorted_a = array('III', 'V', 'XIII', 'XIX', 'LII', 'MCCXCIV');
So my question is: what is the best way to sort an array of Roman numerals? I know how to use the array sorting functions of PHP, I'm interested in the logic that goes on inside the comparison function.
EDIT: For simplicity, I'm only looking for a way that deals with strings constructed of the basic numerals in a standard way (no CCCC for example):
I, V, X, L, C, D, M
TEST RESULTS
I took the time to extensively test all the code examples that were posted. Two tests were taken, one with a random array of 20 Roman numerals, and a second with an array containing 4000 of those. Same machine, lot of iterations, an average time taken, and all this run several times. Of course this is nothing offical, just my own tests.
TEST WITH 20 NUMERALS:
hakre, bazmegakapa - around 0.0005 s
anemgyenge, Andrea, Dirk McQuickly - around 0.0010 s
Joe Nelson - around 0.0050 s
Rob Hruska - around 0.0100 s
TEST WITH 4000 NUMERALS:
hakre, bazmegakapa - around 0.13 s
anemgyenge - around 1.4 s
Dirk McQuickly, Andrea - around 1.8 s
Rob Hruska - around 2.8 s
Joe Nelson - around 15 s (surprise, checked several more times)
I have a hard time awarding the bounty. hakre and I made the fastest versions, following the same route, but he made a variation of mine, which was previously based on borrible's idea. So I will accept hakre's solution, because that is the quickest and nicer than mine (IMO). But I will award the bounty to anemgyenge, because I love his version and a lot of effort seems to be put into it.
Picking your class to convert roman numbers to integers, a user-defined sort callback can handle this to sort the array:
$a = array('XIX', 'LII', 'V', 'MCCXCIV', 'III', 'XIII');
$bool = usort($a, function($a, $b) {
return RomanNumber::Roman2Int($a) - RomanNumber::Roman2Int($b);
});
var_dump($a);
So here you find the logic inside the comparison function: if both values are of the same weight, return 0. If the first is lower than the second, return < 0 (e.g. -1), otherwise the second is larger than the first so return > 0 (e.g. 1).
Naturally any other type of function that returns the decimal value for a roman number would work as well.
Edit:
As you commented, you do not want to run the conversion for each pair. That's fine, with a help of an additional array which contains all converted values, you can run the sort on the decimal values and use that sorting on the roman numbers as well (Demo):
$a = array('XIX', 'LII', 'V', 'MCCXCIV', 'III', 'XIII');
$b = array_map('RomanNumber::Roman2Int', $a);
array_multisort($b, $a);
var_dump($a);
array_multisort PHP Manual does most of the magic here.
function sortRomanNum($a, $b) {
if($a == $b) return 0;
$str = "0IVXLCDM";
$len = 0;
if(strlen($a) >= strlen($b)) {
$len = strlen($a);
$b .= str_repeat("0", $len - strlen($b));
}
else {
$len = strlen($b);
$a .= str_repeat("0", $len - strlen($a));
}
for($i = 0; $i < $len - 1; $i++) {
$a1 = $a[$i]; $b1 = $b[$i]; $a2 = $a[$i+1]; $b2 = $b[$i+1];
if( strpos($str, $a1.$b1.$a2) !== false ) return 1;
if( strpos($str, $b1.$a1.$b2) !== false ) return -1;
if($a1 != $b1) return strpos($str, $a1) > strpos($str, $b1) ? 1 : -1;
}
if($a[$i] != $b[$i]) return strpos($str, $a[$i]) > strpos($str, $b[$i]) ? 1 : -1;
}
Given two numbers (roman strings), $a and $b. If there are no substractions in the numbers (IV, IX, XC etc), then the solution would be trivial:
for all $i in $a and $b
if $a[$i] > $b[$i] then return 1; //($a is greater then $b)
if $a[$i] < $b[$i] then return 1; //($a is lower then $b)
return 0 //equality
Since there can be these special parts, the calculation is more complex. But the solution is to find the patterns:
a: IX | XC | CM
b: V | L | D
These are the only patterns which can mess up the trivial solution. If you find any of these, then $a will be greater then $b.
Note, that roman numbers don't include zeros, like the arabic ones. Therefore now we will use them (and basically put zeros where they are missing).
So here comes the function:
if $a == $b then return 0; //equality
create a string for ordering the roman numerals (strpos will give the right index)
define the length of the loop (take the longer string), and add zeros to the end of the shorter number
run the loop, and check:
1. if the patterns above are found, return the comparision accordingly (1 or -1)
2. otherwise do the trivial check (compare each numeral)
check the last numerals too.
Some people have suggested converting Roman numerals to integers, sorting, and mapping back. There is an easier way. All that we really need to do is compare any two arbitrary Roman numerals and let usort do the rest. Here is the code, and I will explain its design below.
$base = array( 'I' => 0, 'V' => 1, 'X' => 2, 'L' => 3,
'C' => 4, 'D' => 5, 'M' => 6 );
function single($a) { global $base; return $base[$a]; }
function compare($a, $b) {
global $base;
if(strlen($a) == 0) { return true; }
if(strlen($b) == 0) { return false; }
$maxa = max(array_map('single', str_split($a)));
$maxb = max(array_map('single', str_split($b)));
if($maxa != $maxb) {
return $maxa < $maxb;
}
if($base[$a[0]] != $base[$b[0]]) {
return $base[$a[0]] < $base[$b[0]];
}
return compare(substr($a, 1), substr($b, 1));
}
$a = array('XIX', 'LII', 'V', 'MCCXCIV', 'III', 'XIII');
usort($a, compare);
print_r($a);
First we create a lookup array to assign a "magnitude" to single digit Roman numerals. Notice this isn't their decimal value, just numbers assigned in such a way that bigger numerals get bigger values. Then we create a helper function single used by some PHP functions to to retrieve the magnitudes.
OK, now to the meat of the algorithm. It is the compare function which sometimes has to call itself recursively when it needs to break a tie. For this reason, we start with some tests to see if it has reached terminal states in the recursion. Disregard that for now and look at the first interesting test. It checks to see if either numeral being compared has a digit in it that dwarfs any digits of the other. For instance, if one of them has X in it, and the other only has I and V, then the one with X wins. This relies on the convention that certain Roman numerals are not valid, like VV or VIIIII or IIIIIIIII. At least I have never seen them written that way, so I count them as invalid.
To make this check, we map the digits to magnitudes and compare maximums. Well, this test may not decide the issue. In that case it is safe to compare the first digits of each number, since we won't have to deal with confusing issues like V < IX where the first digits don't suggest the truth. These confusing situations were taken care of by comparing largest digits.
Finally, if the first digits are equal, strip them off and repeat. At some point one of the numerals will be reduced to an empty string, and those initial tests we were temporarily disregarding will take care of that.
This method has passed all the tests I threw at it, but let me know if you find a bug or optimizations.
There would seem to be three approaches, namely:
Convert the numbers, sort using a standard integer sort, and convert back. (Or keep the converted versions with the roman numerals and sort the structures, to avoid the double conversion.)
Write a sort function that takes the strings, at that point calls a conversion function and does the appropriate comparison.
Write a sort function that can compare Roman numerals directly, without necessary involving a full conversion. Since Roman numerals have their higher components first, (Ms then D/Cs. then L/Xs, then I/Vs) such a function might be able to short circuit early.
The first will obviously involve additional overhead for storage. The second will involve additional conversion overhead (since the same number may be converted many times). The third might involve some unnecessary conversion overhead (again, the same number may be converted several times) but save some work on the short circuiting. If storage overheads are not an issue, the first is likely to be the best.
I got quite interested in #borrible's 1st approach, so I decided I will give it a try:
function sortRomanArray($array) {
$combined=array_combine($array, array_map('roman2int', $array));
asort($combined);
return array_keys($combined);
}
This basically converts all the Roman numerals in the array into integers using array_map() and a function called roman2int() (which can be any implementation). Then it creates an array where the keys are the Roman numerals and values are the integers. Then this array is sorted with asort() that preserves key associations, and the keys are returned as an array. This array will contain the sorted Roman numerals.
I like this method because it runs the conversion function only as much times as the size of the array (6 with my example array) and there is no need to convert back.
The conversion would run certainly much more if we put it in the comparison function (2 times for every comparison).
I think you'll have to either:
Wrap the strings into a RomanNumeral class, that has a sorting method OR
Write a method to calculate the value of each element in the array, and sort on that
See if someone has already written a RomanNumeral class/library that does this - something like this
Either way, you'll need custom sorting code that calculates the value somewhere. Since prefixing characters in Roman Numerals can sometimes mean "subtract this value" as opposed to "add this value". This is fine, because as you've pointed out, what you're really doing is sorting by numeric value, so you'll have to tell the computer how to interpret the value.
Convert the numeral to a decimal using this
Compare the decimals
function roman2dec($roman) {
// see link above
}
function compare($a, $b) {
return roman2dec($a) < $roman2dec($b) ? -1 : 1;
}
The simplest solution is probably to first convert each numeral into a regular integer (in a new array), and then sort both arrays based on the integer array. Not sure if PHP contains a function for that, though. Alternatively, you can define a comparison function that converts two Roman numerals to integers and compares them. Writing a function that directly compares two Roman numerals without converting them to integers first will likely be cumbersome.
Let's say you make this "alphabet": I, IV, V, IX, X, XL, L, XC, C, CD, D, CM, M.
Then you could sort the Roman numbers according to this 'alphabet'.
Maybe this will give someone new inspiration.
EDIT: got a working example. Not really fast, sorts 1000 Roman numbers in 1.3 secs
EDIT 2: added a check to avoid the 'notices', also optimized the code a little, runs a little faster, and about twice as fast than with a conversion to integer and than sort that (used PEAR Number_Roman package)
function sortromans($a, $b){
$alphabet = array('M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I');
$pos = 0;
if ($a == $b) {
return 0;
}
//compare the strings, position by position, as long as they are equal
while(isset($a[$pos]) && isset($b[$pos]) && $a[$pos] === $b[$pos]){
$pos++;
}
//if string is shorter than $pos, return value
if(!isset($a[$pos])){
return -1;
} else if(!isset($b[$pos])){
return 1;
} else {
//check the ´character´ at position $pos, and pass the array index to a variable
foreach($alphabet as $i=>$ch){
if(isset($a_index) && isset($b_index)){
break;
}
$length = strlen($ch);
if(!isset($a_index) && substr($a, $pos, $length) === $ch){
$a_index = $i;
}
if(!isset($b_index) && substr($b, $pos, $length) === $ch){
$b_index = $i;
}
}
}
return ($a_index > $b_index) ? -1 : 1;
}
$romans = array('III', 'IX', 'I', 'CM', 'LXII','IV');
usort($romans, "sortromans");
echo "<pre>";
print_r($romans);
echo "</pre>";
I think the best (see my comment) first solution is to use the standard usort PHP function with the help of a special roman compare function.
The following roman_compare function is very intuitive and do not use any kind of conversion. To keep it simple, it uses tail recursion.
function roman_start( $a )
{
static $romans = array(
'I' => 1, 'V' => 5,
'X' => 10, 'L' => 50,
'C' => 100, 'D' => 500,
'M' => 1000,
);
return $a[0] . ($romans[$a[0]] < $romans[$a[1]] ? $a[1] : '');
}
function roman_compare( $a, $b )
{
static $romans = array(
'I' => 1, 'IV' => 4, 'V' => 5, 'IX' => 9,
'X' => 10, 'XL' => 40, 'L' => 50, 'XC' => 90,
'C' => 100, 'CD' => 400, 'D' => 500, 'CM' => 900,
'M' => 1000,
);
$blockA = roman_start($a);
$blockB = roman_start($b);
if ($blockA != $blockB)
{
return $romans[$blockA] - $romans[$blockB];
}
$compared = strlen($blockA);
if (strlen($a) == $compared) //string ended
{
return 0;
}
return roman_compare(substr($a, $compared), substr($b, $compared));
}
Using the above functions, we can write
function array_equal( $a, $b )
{
return count(array_diff_assoc($a, $b)) == 0 && count(array_diff_assoc($b, $a)) == 0;
}
$a = array('XIX', 'LII', 'V', 'MCCXCIV', 'III', 'XIII');
$sorted_a = array('III', 'V', 'XIII', 'XIX', 'LII', 'MCCXCIV');
var_dump(array_equal($sorted_a, $a));
usort($a, 'roman_compare');
var_dump(array_equal($sorted_a, $a));
Running all the above code we get
bool(false)
bool(true)

PHP and access to string like to assoc array

Is that logical behavior?
$str = 'string';
$res = $str['some_key'];
echo (int)isset($str['some_key']); // 1
echo $res; // 's'
It's a bug or unclear feature?
It is a "feature". When using $string[$index], $index is treated as integer, so 'some_key' is converted to 0. That's also why you get 's' (first letter of $str) in $res.
$str = 'Lorem';
var_dump($str['key']); // L, because (int)'key' is 0
var_dump($str['0key']); // L
var_dump($str['1key']); // o, because (int)'1key' is 1
var_dump($str['2key']); // r
var_dump($str['3key']); // e, because (int)'3key' is 3
var_dump($str['4key']); // m
var_dump($str['5key']); // Notice: Uninitialized string offset: 5 in sandbox\index.php on line 20
Accessing strings like arrays is a feature.
Strings only have numeric offsets, any "key" you use is cast to an int.
Non-numeric strings cast to the int 0.
Hence $str["foo"] is equivalent to $str[0].
So there is a logic, whether you want to call it logical or not is up to you.
But if you're accessing strings with string keys, something's wrong with your code anyway. ;-)

Substracting float from array

I have an array that contains multiple values with numerical index. The data type for each value is string. It was supposed to be a float number though (38.50, 22.12, etc).
What I want to do now is subtract each values from the array with a number that I'd like to specify myself. And then, find the absolute number in case the subtraction gives a negative number as the result.
How can I do that?
You want to look into the array_map() function.
I don't know if this is what you meant:
$original_array = array('38.50', '22.12');
function sort( $n ){
$num = 12.0;
return (double)$n - $num;
}
$newarray = array_map("sort", $original_array );
Converting an integer or a string to a float can be a pain though.
Using any of these functions will convert them to a proper float first:
function float_format( $number, $decimal = 2, $decimal_sep = '.', $thousands_sep = '' ){
return number_format($number, $decimal, $decimal_sep, $thousands_sep);
}
function float_val( $number, $decimal, $float_locale = 'f' ){
//f - the argument is treated as a float, and presented as a floating-point number (locale aware).
//F - the argument is treated as a float, and presented as a floating-point number (non-locale aware). Available since PHP 4.3.10 and PHP 5.0.3.
return (double)sprintf("%.".$decimal.$float_locale, $number);
}
The first of which is better for currency. Hope this helps.

Categories