7

It appears, that in 32bit OS ip2long returns signed int, and in 64bit OS unsigned int is returned.

My application is working on 10 servers, and some are 32bit and some are 64bit, so I need all them to work same way.

In PHP documentation there is a trick to make that result always unsigned, but since I got my database already full of data, I want to have it signed.

So how to change an unsigned int into a signed one in PHP?

Thinker
  • 14,234
  • 9
  • 40
  • 55
  • Signed `int`s are encoded as [two complements](https://www.cs.cornell.edu/~tomf/notes/cps104/twoscomp.html) meaning that unless you compare with > or < or use arithmetic it really doesn't matter that they have a sign or not. As long as when converting to string you consider that. Your database probably (hopefully) did use unsigned ints reading. But that information is missing from your question. – theking2 Mar 24 '22 at 18:05

7 Answers7

19

PHP does not support unsigned integers as a type, but what you can do is simply turn the result of ip2long into an unsigned int string by having sprintf interpret the value as unsigned with %u:

 $ip="128.1.2.3";
 $signed=ip2long($ip);             // -2147417597 in this example
 $unsigned=sprintf("%u", $signed); //  2147549699 in this example

Edit, since you really wanted it to be signed even on 64 bit systems - here's how you'd convert the 64 bit +ve value to a 32 bit signed equivalent:

$ip = ip2long($ip);
if (PHP_INT_SIZE == 8)
{
    if ($ip>0x7FFFFFFF)
    {
        $ip-=0x100000000;
    }
}
Paul Dixon
  • 295,876
  • 54
  • 310
  • 348
  • That could be found in PHP documentation. And that's opposite of that, what I asked. – Thinker May 16 '09 at 14:45
  • I think that's the solution I needed. I didn't know about PHP_INT_SIZE. Thank's for your edit. – Thinker May 16 '09 at 15:14
  • 1
    The "if (PHP_INT_SIZE == 8)" guard isn't really required - you'll get the result you want on 32 and 64 bit systems without it, but it makes the example more illustrative. – Paul Dixon May 16 '09 at 15:22
4

Fwiw, if you're using MySQL it's usually a lot easier and cleaner if you just pass in the IPs as strings to the database, and let MySQL do the conversion using INET_ATON() (when INSERTing/UPDAT(E)'ing) and INET_NTOA() (when SELECTing). MySQL does not have any of the problems described here.

Examples:

SELECT INET_NTOA(ip_column) FROM t;

INSERT INTO t (ip_column) VALUES (INET_ATON('10.0.0.1'));

The queries are also much more readable.

Note that you can not mix INET_NTOA()/INET_ATON() in MySQL with ip2long()/long2ip() in PHP, since MySQL uses an INT UNSIGNED datatype, while PHP uses a signed integer. Mixing signed and unsigned integers will seriously mess up your data!

2

interpreting an integer value as signed int on 32 and 64 bit systems:

function signedint32($value) {
    $i = (int)$value;
    if (PHP_INT_SIZE > 4)   // e.g. php 64bit
        if($i & 0x80000000) // is negative
            return $i - 0x100000000;
    return $i;
} 
epos_jk
  • 289
  • 2
  • 6
1
public function unsigned2signed($num) {     // converts unsigned integer to signed
    $res = pack('i',$num);                  // change to 'l' to handle longs
    $res = unpack('i',$res)[1];
    return $res;
}
atyachin
  • 1,103
  • 1
  • 12
  • 14
1

-Misunderstood problem, see Paul Dixon's answer above.


64bit unsigned integers are not technically supported in PHP5. It will use the native type. To convert to a 32bit signed int from a 64bit signed int without losing the high bit, you could mask and then type cast the output:

$ip_int = ip2long($ip);
if (PHP_INT_SIZE == 8) // 64bit native
{
  $temp_int = (int)(0x7FFFFFFF & $ip_int);
  $temp_int |= (int)(0x80000000 & ($ip_int >> 32));
  $ip_int = $temp_int;
}

On a 64 bit system, printing this value ($ip_int) will display an 'unsigned' integer since we've removed the high bit. However this should allow you to take the output and store it how you wish.

cyberbill
  • 134
  • 1
  • 7
  • echo (int)(0xFFFFFFFF & -2147417597) still displays -2147417597 on a 32 bit system. – Paul Dixon May 16 '09 at 14:27
  • On a 64 bit system, the return value of ip2long is always positive. That's the nature of the OP's problem: on 64bit systems he always get a +ve value, but on 32bit systems he gets some -ve values. – Paul Dixon May 16 '09 at 14:54
  • I had misread the 'unsigned' nature of his question. I had believed the output was coming back signed since there are no unsigned ints. I'd vote up your now modified answer, perfect, but do not have enough points – cyberbill May 16 '09 at 15:13
  • This is not working, but the idea with PHP_INT_SIZE was good. Your code is always returning positive values, but a signed value was my need. – Thinker May 16 '09 at 15:16
0

unsigned-int is a php library to convert signed integers to unsigned. (Disclaimer: I'm the author)

use Oct8pus\Unsigned\UInt32;

require_once './vendor/autoload.php';

$uint32 = new UInt32(-2147483648);
echo $uint32;
-2147483648 > 0x80000000 (2147483648)

The repository is here https://github.com/8ctopus/unsigned-int

8ctopus
  • 2,617
  • 2
  • 18
  • 25
0

Static cast to 32-bit signed integer. Works on 64-bit and 32-bit PHP.

function toSInt32($a) {
  return ~0x7FFFFFFF * (($a >> 31) & 1) + ($a & 0x7FFFFFFF);
}

--------

var_dump(toSInt32(0));
var_dump(toSInt32(1));
var_dump(toSInt32(0x7FFFFFFF));
var_dump(toSInt32(0x80000000));
var_dump(toSInt32(0x8000000000));
var_dump(toSInt32(0xFFFFFFFF));
var_dump(toSInt32(0xFFFFFFFFFF));

--------

int(0)
int(1)
int(2147483647)
int(-2147483648)
int(0)
int(-1)
int(-1)
NoSkill
  • 718
  • 5
  • 15