在 32 位 PHP 中处理大整数


Coping with big ints in 32 bit PHP

我有一个用于计算数字的 Luhn 校验和的类。 它采用整数作为输入并返回 true 或 false 以指示有效性或其他方式,或者如果将不适当的数据类型作为输入,则会引发异常。

代码如下(完整源代码在 GitHub 上(:

class Luhn extends abstr'Prop implements iface'Prop
{
    /**
     * Test that the given data passes a Luhn check. 
     * 
     * @return bool True if the data passes the Luhn check
     * @throws 'InvalidArgumentException 
     * @see http://en.wikipedia.org/wiki/Luhn_algorithm
     */
    public function isValid ()
    {
        $data   = $this -> getData ();
        $valid  = false;
        switch (gettype ($data))
        {
            case 'NULL'     :
                $valid  = true;
            break;
            case 'integer'  :
                // Get the sequence of digits that make up the number under test
                $digits = array_reverse (array_map ('intval', str_split ((string) $data)));
                // Walk the array, doubling the value of every second digit
                for ($i = 0, $count = count ($digits); $i < $count; $i++)
                {
                    if ($i % 2)
                    {
                        // Double the digit
                        if (($digits [$i] *= 2) > 9)
                        {
                            // Handle the case where the doubled digit is over 9
                            $digits [$i]    -= 10;
                            $digits []      = 1;
                        }
                    }
                }
                // The Luhn is valid if the sum of the digits ends in a 0
                $valid  = ((array_sum ($digits) % 10) === 0);
            break;
            default         :
                // An attempt was made to apply the check to an invalid data type
                throw new 'InvalidArgumentException (__CLASS__ . ': This property cannot be applied to data of type ' . gettype ($data));
            break;
        }
        return ($valid);
    }
}

我还构建了一个完整的单元测试来练习这门课。

我的主要开发环境是一个在OSX Lion下运行64位构建PHP 5.3和Apache的工作站。 我还使用一台笔记本电脑,该笔记本电脑也在Apache下运行Apache的64位版本和PHP 5.4。 除此之外,我还有一个运行64位Apache和PHP 5.3的Ubuntu Linux虚拟机。 正如预期的那样,单元测试对所有这些都很好。

我想我可以在午餐时间工作(Windows 7,XAMPP,32位PHP 5.3(抽出时间来处理这个类所属的项目,但我遇到的第一件事是单元测试失败。

问题是,在 PHP 的 32 位构建中,如果数字超过 32 位整数的限制,则数字会被静默转换为浮点数。 我提出的解决方案是有一个浮点的特殊情况。 如果输入类型为浮点数,并且其值超出了可以用 int (PHP_INT_MIN 表示的范围。PHP_INT_MAX( 然后我会number_format(( 它以将其恢复为一串数字。 如果它在整数范围内,那么我将抛出一个异常。

但是,这导致了它自己的问题。 我知道浮点数离 0 越远,该数字的分辨率就越小(给定数字和下一个可表示数字之间的增量越小(。 在无法再可靠地表示整数部分之前,您必须离 0 多远才能表示数字的整数部分? (我不确定这是否真的清楚,例如,假设在分辨率低于一个 int 和下一个 int 之间的差异之前,限制为 1000。 我可以输入一个大于 1000 的数字,比如 1001,但浮点数的限制意味着它最终是 1001.9,四舍五入得到 1002,这意味着我已经失去了我感兴趣的值(。

是否可以检测分辨率损失何时会成为浮点数的问题?

编辑为 ADD:我想我可以修改扩展名以接受字符串而不是数字类型,然后使用正则表达式或其他类似技术验证它是否仅包含数字,但由于 Luhn 可检查数据是一串数字对我来说感觉不对,不知何故。 有一些 PHP 扩展可以处理 bignums,但由于它们是扩展,并且这是一段框架代码,可能会部署在各种配置中,如果可能的话,我宁愿不依赖此类扩展的存在。 此外,以上都没有解决如果你给 PHP 一个大整数的问题,它会静默地将其转换为浮点数。 我需要一种方法来检测这种情况是否已经发生。

如果需要精度,则不应使用浮点数。

相反,特别是当你想使用整数时(如果我理解正确的话(,你可以尝试使用bc*函数: BCMath 任意精确数学

如果需要精度,则不应使用浮点数。

相反,特别是当你想使用整数时(如果我理解正确的话(,你可以尝试使用gmp*函数: GMP - GNU 多重精度

如果您无法使用该扩展程序,您可能会从

  • 梨大整数 - 纯PHP任意精度整数算术库