2017-10-17 41 views
-1

Je travaille sur un script PHP qui gère les montants monétaires, et doit donc être exact avec 2 décimales. Pour ce faire, je convertis l'entrée utilisateur en un nombre en le multipliant par 100, puis en le convertissant en int. Cela fonctionne bien, jusqu'à ce que j'ai récemment découvert un nombre qui augmente de 1 lorsqu'il est converti en int.PHP (int) génère un mauvais numéro

le code défectueux:

$number = (int)(str_replace(',','.',$_POST["input"])*100); 

Le nombre qui donne des problèmes est 2509,22 (je vis aux Pays-Bas, nous utilisons donc une virgule est pour décimaux, d'où la str_replace dans la ligne de code ci-dessus). Cette valeur crée le nombre entier $ 250921, ce qui est évidemment 1 trop faible. Je sais que int a des limites, mais ce nombre est bien en deçà des limites pour autant que je sache ...

+0

Pourquoi avez-vous besoin d'en faire un int? Quel est le but de faire cela? Qu'est-ce qui se passe à côté de cet int? – Andreas

+0

Je l'utilise pour m'assurer que l'entrée ne contient pas plus de 2 décimales. Comme indiqué, il s'agit d'informations financières, et ceci afin d'empêcher l'utilisateur d'entrer des fractions plus petites que possible dans les systèmes financiers. – Discoverer

+1

Utilisez plutôt bcmath. Ne jamais opérer sur des flotteurs sauf si vous êtes absolument sûr de la sortie et en juger par votre code, je suis sûr que vous ne l'êtes pas. Vous pouvez en savoir plus sur la précision du nombre flottant ici http://php.net/manual/fr/language.types.float.php –

Répondre

-1

Lorsque vous multipliez nombre par 100, vous ne recevez pas réellement 250922 mais sometging littlebit inférieur (250921,999 ....)

(int) chaque fois arrondir si vous devez faire un tour avant "int-ing":

$number = intval(round(str_replace(',','.',"2509,22")*100)); 
+0

Cela fait l'affaire. Dans d'autres réponses, j'ai déjà appris que 2809.22 * 100 aboutit au nombre à virgule flottante 250921.99999999997, et lorsqu'on les place directement dans int, les décimales sont supprimées. Votre ajout de "rond" a résolu cela. – Discoverer

+0

Non, rond ne le résout pas '2509,225' devient' 250923'. – AbraCadaver

2

Lorsque vous multipliez la chaîne par 100 vous obtenez un float et sa représentation n'est pas toujours ce que vous attendez , dans ce cas 250921.99999999997. Voir avec:

echo serialize(str_replace(',','.','2509,22')*100); 

Ensuite, vous lancez une integer qui trucates la fraction pour obtenir 250921. Voir Is floating point math broken?.

La solution serait de supprimer la virgule et utiliser comme est et éventuellement castée integer:

$number = (int)str_replace(',', '', '2509,22'); 

Pour la question des utilisateurs qui entrent trop de nombres fractionnaires, vous devez utiliser soit deux entrées, une pour nombre entier et un pour fractionner et/ou restreindre/valider que les entrées sont correctement formatées. Toutefois, vous pouvez d'abord mettre en forme le nombre:

echo $number = (number_format(str_replace(',', '.', '2509,22'), 2, '.', '')*100); 
+0

D'accord, merci! et le nombre entier arrondit vers le bas je présume. Savez-vous quel type de casting je devrais utiliser pour obtenir le bon numéro? – Discoverer

+1

Parce que quand un utilisateur appuie sur un bouton une fois de trop (donc il met 2509 222 dans la boîte d'entrée), le nombre qui sera stocké sera beaucoup trop élevé. – Discoverer

+0

@Discoverer 1) Rejeter l'entrée s'il y a plus de deux chiffres après la virgule (ou virgule :) 2) Fournir la vérification de la valeur au client avant de continuer et/ou autoriser une méthode d'annulation. L'utilisateur peut avoir utilisé par inadvertance le "mauvais" séparateur * et * tapé un nombre supplémentaire. – user2864740

0

Vous pouvez utiliser regex pour faire correspondre les nombres décimaux int et zéro.
Cela ne fera aucune conversion et rien n'est multiplié ou casté.
Il sera traité comme une chaîne de caractères et rien ne changera si ce n'est le nombre de décimales.

$input = "2509,2222222"; 
// Regex number comma number (zero - two) 
Preg_match("/(\d+),*(\d{0,2})/", $input, $m); 
Unset($m[0]); // remove full match 

Echo implode("", $m); // implode parts 

https://3v4l.org/MjcYV

+0

Le problème est que les chiffres devraient également être autorisés à entrer avec un point au lieu d'une virgule. Bien sûr, vous pouvez également supprimer cela, mais quand un utilisateur entre un mauvais montant (disons 2509 222 en appuyant une fois sur le bouton), le montant serait 2509222, ce qui est beaucoup trop élevé. – Discoverer

+0

Cela pourrait fonctionner en effet. Personnellement, je pense que je vais utiliser la solution suggérée par David Bubenik, car elle permet également d'entrer des chiffres sans décimales. Les utilisateurs ne sont pas tous très précis dans leurs façons, alors j'essaye de rendre cela aussi flexible que possible, mais merci pour la suggestion! – Discoverer

+0

@Discoverer mec. Lorsque vous publiez une question, publiez toutes les informations dont nous avons besoin pour vous aider. Vous continuez d'ajouter des informations et c'est vraiment ennuyeux. – Andreas