php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Doc Bug #76098 Conversion from hex string to 64-bit int returns wrong value
Submitted: 2018-03-15 08:47 UTC Modified: 2018-03-16 04:29 UTC
From: mikael dot knutsson at gmail dot com Assigned:
Status: Open Package: Math related
PHP Version: 7.2.3 OS: Linux 64-bit
Private report: No CVE-ID: None
View Add Comment Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
You can add a comment by following this link or if you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
Package:
Bug Type:
Summary:
From: mikael dot knutsson at gmail dot com
New email:
PHP Version: OS:

 

 [2018-03-15 08:47 UTC] mikael dot knutsson at gmail dot com
Description:
------------
We have a hex string like this: "eb3dd796efda2409" received from an upstream system. This is a big-nibble hex string containing this integer value: -1495802457948019703
This is a 64-bit signed integer.

When converting this using hexdec this is converted to a float and returns the wrong value, when assigned to a variable as a hex string and converted using intval() it loses precision.

When looking at the documentation: http://php.net/manual/en/function.hexdec.php it mentions this:
> The function can convert numbers that are too large to fit into the platforms integer type, larger values are returned as float in that case.

However, this happens on a 64-bit compiled PHP where this integer should fit into the platform's integer size.
php --version
PHP 7.2.3-1 (cli) (built: Mar  6 2018 11:15:04) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies
    with Zend OPcache v7.2.3-1, Copyright (c) 1999-2018, by Zend Technologies
php -a
Interactive mode enabled

php > echo PHP_INT_MAX;
9223372036854775807
php > echo PHP_INT_SIZE;
8

As a side note:
We ended up using pack/unpack to avoid the interim float conversion, and we also had to use "J" as the unpack conversion type to get the right value which implies big-endianness in the packed value. I am not sure this is correct either as doing this conversion in other languages works just fine without specifying endianness. (tried in Python and Go)
(I can file a separate bug if this is also not intended)

Test script:
---------------
$strhex = "eb3dd796efda2409";
$hex = 0xeb3dd796efda2409;

echo hexdec($strhex);
echo $hex;
echo intval($hex);

var_dump(unpack('J', pack("H*", $strhex)));


Expected result:
----------------
-1495802457948019703
-1495802457948019703
-1495802457948019703
array(1) {
  [1]=>
  int(-1495802457948019703)
}

Actual result:
--------------
1.6950941615762E+19
1.6950941615762E+19
-1495802457948020736
array(1) {
  [1]=>
  int(-1495802457948019703)
}

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2018-03-15 11:24 UTC] cmb@php.net
-Type: Bug +Type: Documentation Problem -Package: Variables related +Package: Math related
 [2018-03-15 11:24 UTC] cmb@php.net
> […] which implies big-endianness in the packed value.

That is to be expected.  `pack("H*", $strhex)` returns
"\xeb\x3d\xd7\x96\xef\xda\x24\x09", which is big-endian
(the highest byte is to the left).

> However, this happens on a 64-bit compiled PHP where this
> integer should fit into the platform's integer size.

No, it does not, since "PHP does not support unsigned
integers"[1].

> when assigned to a variable as a hex string and converted using
> intval() it loses precision.

Hexadecimal number literals are regarded as non-negative numbers,
but this number exceeds PHP_INT_MAX, so it is stored as float.
Since the float value exceeds PHP_INT_MAX the result of the
conversion to integer is actually undefined[2].

So yes, if you want to convert unsigned 64bit values represented
as hexadecimal strings, you have to work-around the signed integer
limitations.  Your (un)pack() solution appears to be appropriate.

Changing to doc-bug, since this info could be added to the
hexdec() man page.

[1] <http://www.php.net/manual/en/language.types.integer.php#language.types.integer.syntax>
[2] <http://www.php.net/manual/en/language.types.integer.php#language.types.integer.casting.from-float>
 [2018-03-16 04:29 UTC] mikael dot knutsson at gmail dot com
Thank you for clarifying!
 
PHP Copyright © 2001-2019 The PHP Group
All rights reserved.
Last updated: Tue Apr 23 04:01:25 2019 UTC