数値の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);
}
}
また、クラスを実行するため の完全な単体テストを作成しました。
私の主な開発環境は、OSXLionで64ビットビルドのPHP5.3とApacheを実行するワークステーションです。また、ApacheとPHP5.4の64ビットビルドを実行しているラップトップもApacheで使用しています。これに加えて、64ビットのApacheとPHP5.3を実行しているUbuntuLinux仮想マシンがあります。予想通り、ユニットテストはこれらすべてで問題ありませんでした。
このクラスが含まれているプロジェクト(Windows 7、XAMPP、32ビットPHP 5.3)で昼食をとる時間はあると思いましたが、最初に遭遇したのは単体テストの失敗でした。
問題は、PHPの32ビットビルドでは、32ビット整数の制限を超えると、数値がサイレントにキャストされて浮動小数点になることです。私が提案する解決策は、floatの特別なケースを用意することです。入力型がfloatであり、その値がint(PHP_INT_MIN .. PHP_INT_MAX)で表現できる範囲外の場合は、number_format()を実行して、数字の文字列に戻します。整数の範囲内であれば、例外をスローします。
しかし、これはそれ自身の問題につながります。浮動小数点数で0から離れるほど、その数の解像度は低くなります(特定の数と次の表現可能な数の間の増分が小さくなります)。整数部分を確実に表現できなくなる前に、数値の整数部分を表現できなくなる前に、0からどれだけ離れている必要がありますか?(それが本当に明確かどうかはわかりません。たとえば、解像度が1つの整数と次の整数の差を下回る前に制限が1000であると言います。1000より大きい桁、たとえば1001を入力できますが、浮動小数点数は、最終的に1001.9になり、丸めると1002になることを意味します。これは、関心のある値を失ったことを意味します)。
解像度の低下が浮動小数点数の問題になる時期を検出することは可能ですか?
追加するために編集:拡張機能を変更して、数値型の代わりに文字列を受け入れ、正規表現または他の同様の手法で数字のみが含まれていることを確認できると思いますが、Luhnでチェック可能なデータは数字の文字列であり、どういうわけか、私には正しく感じません。PHPにはbignumを処理できる拡張機能がありますが、これらは拡張機能であり、さまざまな構成にデプロイできる可能性のあるフレームワークコードの一部であるため、そのような存在に依存したくありません。可能であれば拡張機能。さらに、上記のいずれも、PHPに大きなintを与えると、サイレントにそれをfloatに変換するという問題に対処していません。これが起こったことを検出する方法が必要です。