Question

J'ai un cours pour calculer la somme de contrôle de Luhn pour un nombre.Il prend un entier comme entrée et renvoie vrai ou faux pour indiquer la validité ou autre, ou il lève une exception si un type de données inapproprié est donné en entrée.

Le code est le suivant (La source complète est sur 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);
    }
}

J'ai aussi construit un plein Test de l'unité pour exercer la classe.

Mon environnement de développement principal est un poste de travail exécutant des versions 64 bits PHP 5.3 et Apache sous OSX Lion.J'utilise également un ordinateur portable exécutant une version 64 bits d'Apache et PHP 5.4 également sous Apache.De plus, j'ai une machine virtuelle Ubuntu Linux exécutant Apache 64 bits et PHP 5.3.Le test unitaire s'est bien passé pour tout cela, comme prévu.

Je pensais pouvoir disposer d'un peu de temps libre pendant le déjeuner au travail (Windows 7, XAMPP, PHP 5.3 32 bits) pour travailler sur le projet dont cette classe fait partie, mais la première chose que j'ai rencontrée a été l'échec du test unitaire.

Le problème est que sur une version 32 bits de PHP, le nombre est converti silencieusement en flottant s'il dépasse les limites d'un entier 32 bits.La solution que je propose est d'avoir un cas spécial pour float.Si le type d'entrée est float et que sa valeur est en dehors de la plage qui peut être exprimée en int (PHP_INT_MIN ..PHP_INT_MAX) puis je vais le number_format() pour le remettre dans une chaîne de chiffres.Si c'est dans la plage d'un nombre entier, je lancerai une exception.

Cependant, cela entraîne son propre problème.Je sais que plus vous vous éloignez de 0 avec un nombre à virgule flottante, moins le nombre a de résolution (plus l'incrément entre un nombre donné et le nombre représentable suivant est petit).À quelle distance de 0 devez-vous vous éloigner avant qu'il devienne impossible de représenter la partie entière du nombre avant de ne plus pouvoir représenter de manière fiable la partie entière ?(Je ne sais pas si c'est vraiment clair, alors par exemple, disons que la limite est de 1 000 avant que la résolution ne descende en dessous de la différence entre un int et le suivant.Je pourrais saisir un chiffre supérieur à 1 000, disons 1 001, mais les limitations des nombres à virgule flottante font qu'il finit par être 1 001,9 et l'arrondir donne 1 002, ce qui signifie que j'ai perdu la valeur qui m'intéressait).

Est-il possible de détecter quand la perte de résolution deviendra un problème pour un nombre à virgule flottante ?

MODIFIER POUR AJOUTER:Je suppose que je pourrais modifier l'extension pour accepter une chaîne au lieu d'un type numérique, puis vérifier qu'elle ne contient que des chiffres avec une expression régulière ou une autre technique similaire, mais comme les données vérifiables par Luhn sont une chaîne de chiffres qui ne semble pas correcte pour moi, d'une manière ou d'une autre.Il existe des extensions pour PHP qui peuvent gérer les bignums, mais comme ce sont des extensions et qu'il s'agit d'un morceau de code-cadre qui pourrait potentiellement être déployé sur un large éventail de configurations, je préfère ne pas compter sur la présence de tels extensions. extensions si possible.En outre, aucun des éléments ci-dessus ne résout le problème selon lequel si vous donnez à PHP un gros int, il le convertit silencieusement en float.J'ai besoin d'un moyen de détecter que cela s'est produit.

Était-ce utile?

La solution

Si vous avez besoin de précision, vous ne devez pas utiliser de flotteurs.

Au lieu de cela, d'autant plus que vous souhaitez travailler avec des entiers (Si je comprends bien), vous pouvez essayer de travailler avec le gmp* les fonctions: GMP - GNU Précision Multiple

Si vous ne pouvez pas travailler avec cette extension, vous pourriez obtenir des idées supplémentaires auprès de

Autres conseils

Si vous avez besoin de précision, vous ne devez pas utiliser de flotteurs.

Au lieu de cela, surtout comme vous voulez travailler avec des entiers (si je comprends correctement) , vous pouvez essayer de travailler avec les fonctions bc*: BCMATH Mathématiques de précision arbitraire

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top