我有一门课来计算一个数字的 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 的工作站。我还使用一台运行 64 位版本的 Apache 和 PHP 5.4 的笔记本电脑,也在 Apache 下运行。除此之外,我还有一个运行 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,这意味着我失去了我感兴趣的值)。
是否有可能检测到分辨率的损失何时会成为浮点数的问题?
编辑添加:我想我可以修改扩展名以接受字符串而不是数字类型,然后验证它是否仅包含使用正则表达式或其他类似技术的数字,但由于 Luhn-checkable 数据是一串数字不知何故,我觉得不对劲。有一些 PHP 扩展可以处理 bignums,但由于它们是扩展,并且这意味着它是一段可能部署在各种配置上的框架代码,我宁愿不依赖这样的存在尽可能扩展。此外,以上都没有解决如果你给 PHP 一个大的 int 它会默默地将它转换为 float 的问题。我需要一种检测这种情况发生的方法。