2017-08-16 2 views
-2

Pour arrondir au 0,05 le plus proche, je le divise par 0,05 et multiplie par 0,05. Comme décrit dans d'autres questions similaires.Boîtier étrange tout en arrondissant au 0,05 le plus proche - cas particulier

In [119]: (127.651//0.05)*0.05 
Out[119]: 127.65 

In [120]: (127.6501//0.05)*0.05 
Out[120]: 127.65 

In [121]: (127.65000001//0.05)*0.05 
Out[121]: 127.65 

In [122]: (127.65000000001//0.05)*0.05 
Out[122]: 127.65 

In [123]: (127.6500000000001//0.05)*0.05 
Out[123]: 127.65 

Jusqu'à ce que ce soit fait comme prévu. Toutefois, pour ce cas particulier:

In [124]: (127.650000000000000001//0.05)*0.05 
Out[124]: 127.60000000000001 

Je me serais attendu 127,65 ici. Essayé d'arrondir avant de diviser, mais encore une fois .. étrange. Non seulement j'obtiens un résultat inattendu (attendu 127.65), mais il donne également des résultats qui dépassent 2 décimales, ce qui entraînera une défaillance dans mes fonctions de traitement ultérieures.

In [125]: (round(127.650000000000000001,5)//0.05)*0.05 
Out[125]: 127.60000000000001 

Si j'exécute seulement la ronde intérieure, o/p est 127,65 ...

In [126]: round(127.650000000000000001,5) 
Out[126]: 127.65 

Mais sur l'ajout de la division et de multiplication logique .. le résultat devient inattendu.

In [125]: (round(127.650000000000000001,5)//0.05)*0.05 
Out[125]: 127.60000000000001 

Devrait-il y avoir un problème de type de données? ou limitations internes de précision de python?

Comment puis-je le surmonter d'une manière élégante?

PS:

1) J'utilise Python 2.7

2) Je suis finalement l'utiliser dans une fonction. Ce qui donne des résultats inattendus pour ce cas particulier. Je ne peux pas contrôler la qualité des données d'entrée et la précision de ses données, car cela provient de la lecture d'un fichier csv dans une trame de données.

def RP(price):# Round down to the nearest price quotable ...i.e. multiple of 0.05 
     return(round(price//0.05*0.05,2)) 
+1

Plus classique [Les nombres à virgule flottante sont-ils brisés?] (Https://stackoverflow.com/q/588004/3001761). Si vous voulez une représentation exacte, pensez à utiliser ['Decimal'] (https://docs.python.org/3/library/decimal.html). – jonrsharpe

+0

Je suis très nouveau à py, mais je suppose que pour les points flottants, il y aura des problèmes. l'importation de décimales le rendra plus précis, je suppose. Juste 'importer décimal' – ihpar

+0

Indice: que vous donne 127,6500000000000056 == 127,65'? Est-ce que la réponse vous surprend? –

Répondre

0

Enfin la logique de mise à l'échelle mentionnée dans is-floating-point-math-broken a fonctionné pour moi.

In [200]: (int(127.65*100)//5*5)/100.0 
Out[200]: 127.65 

In [201]: 

In [201]: (int(127.65000000000000001*100)//5*5)/100.0 
Out[201]: 127.65 

ou:

In [286]: int(127.65*20)/20.0 
Out[286]: 127.65 

In [287]: int(127.6500000000000000000000001*20)/20.0 
Out[287]: 127.65 

MISE À JOUR:

Pour ma destination, (arrondi à 0,05 pour le plus proche obtenir le devis), jusqu'à présent ci-dessous est la plus proche solution que je peux trouver après tant de jours de recherche et de vérification des résultats sur plusieurs valeurs et itérations.

def RP(price):# Round to the nearest price quotable ...i.e. multiple of 0.05 
     return(round(price*20.0,0)/20.0) 

def RP_down(price):# Round_Down to the price quotable ...i.e. multiple of 0.05 
     return(int(price*20.0)/20.0) 

def RP_up(price):# Round_UP to the price quotable ...i.e. multiple of 0.05 
     return(0.05 + int(price*20.0)/20.0) 

Pourquoi ça marche? L'explication ci-dessous est la suivante:

Multiplier par 20,0 suivi de round() ou int(), change le domaine de virgule flottante en entiers.

Et ainsi, élimine le problème de précision, qui a conduit aux résultats inattendus.

Note: Dans la dernière étape (diviser par 20.0), nous passons à nouveau d'un nombre entier à un nombre à virgule flottante, mais avec la valeur attendue correcte (pas de surprises jusqu'à présent après la production dans la dernière semaine). Et vous le savez à coup sûr qu'il n'a théoriquement que 2 décimales (un entier divisé par 20), donc si besoin est, vous pouvez le gérer dans le reste de votre programme en coupant le reste, tout en imprimant.

+1

Ce n'est pas une bonne solution. Cela peut «marcher» pour vous dans les deux cas que vous avez testés, mais vous finirez par trouver d'autres exemples où les choses tournent autour du «mauvais» chemin. –

+0

@Mark Mise à jour de la réponse. Malheureusement, d'autres façons d'utiliser 'Decimal' ne peuvent pas fonctionner pour moi parce que l'entrée de données est déjà en flottant et la précision est déjà perdue. Étonnamment, il y a beaucoup de documentation sur ce problème, mais la majorité consiste à expliquer le problème. Très peu de solutions. Donc, la logique de mise à l'échelle était la meilleure option (je peux me tromper) que j'aurais pu utiliser pour gérer ce cas. Si vous avez une autre solution, s'il vous plaît transmettre. – Wacao