array_unique具有SORT_NUMBERIC行为

问题描述 投票:5回答:2

我偶然发现了一些奇怪的东西,但我不明白为什么它会那样工作。

我有一个数字数组,它们都是唯一的:

$array = [
    98602142989816970,
    98602142989816971,
    98602142989816980,
    98602142989816981,
    98602142989816982,
    98602142989816983,
    98602142989820095,
    98602142989820096,
    98602142989822060,
    98602142989822061,
];
var_dump($array);
array(10) {
  [0]=>
  int(98602142989816970)
  [1]=>
  int(98602142989816971)
  [2]=>
  int(98602142989816980)
  [3]=>
  int(98602142989816981)
  [4]=>
  int(98602142989816982)
  [5]=>
  int(98602142989816983)
  [6]=>
  int(98602142989820095)
  [7]=>
  int(98602142989820096)
  [8]=>
  int(98602142989822060)
  [9]=>
  int(98602142989822061)
}

如果我print_r(array_unique($array));一切都很好,我会得到:

Array
(
    [0] => 98602142989816970
    [1] => 98602142989816971
    [2] => 98602142989816980
    [3] => 98602142989816981
    [4] => 98602142989816982
    [5] => 98602142989816983
    [6] => 98602142989820095
    [7] => 98602142989820096
    [8] => 98602142989822060
    [9] => 98602142989822061
)

但是如果我添加SORT_NUMERIC标志print_r(array_unique($array, SORT_NUMERIC));,则会得到:

Array
(
    [0] => 98602142989816970
    [6] => 98602142989820095
    [8] => 98602142989822060
)

为什么只返回这三个数字?

更新:我在64位系统上。

对于sort函数,我手动调整了一些值,因为在原始数组中它们已经被排序。

如果我执行sort($array);,则响应是预期的:

Array
(
    [0] => 98602142989816970
    [1] => 98602142989816971
    [2] => 98602142989816980
    [3] => 98602142989816981
    [4] => 98602142989816982
    [5] => 98602142989816983
    [6] => 98602142989820095
    [7] => 98602142989820096
    [8] => 98602142989822060
    [9] => 98602142989822061
)

但是使用sort($array, SORT_NUMERIC);,它们的排序不正确:

Array
(
    [0] => 98602142989816970
    [1] => 98602142989816982
    [2] => 98602142989816983
    [3] => 98602142989816980
    [4] => 98602142989816981
    [5] => 98602142989816971
    [6] => 98602142989820095
    [7] => 98602142989820096
    [8] => 98602142989822060
    [9] => 98602142989822061
)
php arrays array-unique
2个回答
5
投票

您在这种规模上遇到了精度和浮点运算的问题。如果您有兴趣,可以在Is floating point math broken?处获得更多的可用信息,但我认为这不算是重复的内容。

取您的前两个数字:

php > var_dump((float) 98602142989816970 === (float) 98602142989816971);
bool(true)

php > var_dump((float) 98602142989816970, (float) 98602142989816971);
float(9.8602142989817E+16)
float(9.8602142989817E+16)

内部,这是当PHP使用SORT_NUMERIC深入到numeric_compare_function中的数组比较数组中的值时发生的情况。

[numeric_compare_function遇到相同的问题,请参见sort(显然不会从数组中删除任何值,因为仅在https://3v4l.org/02UUB中会发生-它们未正确排序)]

简而言之,对于这种大小的数字(或者特别是非常接近的数字),array_unique不会可靠。如果可以的话,坚持将它们作为字符串进行比较。


0
投票

[代码是在32位PHP还是64位版本下运行都有所不同,因为该整数也是32位或64位长。

SORT_NUMERIC

32位系统的结果:

$array = [
    98602142989816970,
    98602142989816971,
    98602142989816980,
    98602142989816981,
    98602142989816982,
    98602142989816983,
    98602142989820095,
    98602142989820096,
    98602142989822060,
    98602142989822061,
];
echo '<pre>';
var_dump(PHP_INT_MAX,$array);

PHP将值转换为立即浮动,因为它们都大于PHP_INT_MAX。

64位系统的结果:

int(2147483647)
array(10) {
  [0]=>
  float(9.8602142989817E+16)
  [1]=>
  float(9.8602142989817E+16)
  [2]=>
  float(9.8602142989817E+16)
  [3]=>
  float(9.8602142989817E+16)
  [4]=>
  float(9.8602142989817E+16)
  [5]=>
  float(9.8602142989817E+16)
  [6]=>
  float(9.860214298982E+16)
  [7]=>
  float(9.860214298982E+16)
  [8]=>
  float(9.8602142989822E+16)
  [9]=>
  float(9.8602142989822E+16)
}

32位系统中的array_unique减少了数组,因为某些值也超过了float的精度。

如果不使用SORT_NUMERIC选项,则array_unique()和sort()对于64位版本可以正常工作。

© www.soinside.com 2019 - 2024. All rights reserved.