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
Votes:1
Avg. Score:5.0 ± 0.0
Reproduced:1 of 1 (100.0%)
Same Version:0 (0.0%)
Same OS:1 (100.0%)
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
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
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

Pull Requests

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!
 [2020-07-05 17:37 UTC] marius dot buescher at gmx dot de
This should be documented though. I also ran into that problem.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Sat Dec 21 17:01:58 2024 UTC