Redondeo de Python inconsistentemente

Si le digo a Python v. 3.4.3, round(2.5) , entonces da como resultado 2 . Sin embargo, si le doy una round(1.5) , también sale 2. De manera similar, la round(3.5) da 4 , mientras que la round(4.5) da 4 . Aunque necesito Python para redondear con consistencia . Específicamente, debe redondearse cada vez que ingrese un número a mitad de camino entre dos enteros. Entonces, round(1.5) = 1 y round(2.5) = 2 , mientras que round(1.6) = 2 y así, como de costumbre.

¿Cómo puedo resolver esto?

EDITAR: Ya he leído la documentación de la función de round y entiendo que este es su comportamiento deseado. Mi pregunta es, ¿cómo puedo alterar este comportamiento, porque para mis propósitos necesito 1.5 redondeo hacia abajo?

Los documentos redondos abordan las peculiaridades del redondeo de números de punto flotante.

Puedes usar la biblioteca decimal para lograr lo que quieres.

 from decimal import Decimal, ROUND_HALF_UP, ROUND_HALF_DOWN round(2.675, 2) # output: 2.67 Decimal('2.675').quantize(Decimal('1.11'), rounding=ROUND_HALF_UP) # output: 2.68 Decimal('2.5').quantize(Decimal('1.'), rounding=ROUND_HALF_DOWN) # output: 2 

Tu deseo “redondear hacia abajo”, y estás obteniendo “redondeo a la par”. Solo hazlo manualmente haciendo

 ceil(x - 0.5) 

Python 3 utiliza un comportamiento de redondeo diferente al de Python 2: ahora usa el llamado “redondeo bancario” ( Wikipedia ): cuando la parte entera es impar, el número se redondea desde cero; cuando la parte entera es par, se redondea hacia cero.

La razón de esto es evitar un sesgo, cuando todos los valores en .5 se redondean desde cero (y luego se sumn, por ejemplo).

Este es el comportamiento que está viendo, y de hecho es consistente. Es quizás diferente a lo que estás acostumbrado.

Esto está documentado bastante bien. Según los documentos de Python para la round :

Nota El comportamiento de round () para flotadores puede ser sorprendente: por ejemplo, round (2.675, 2) da 2.67 en lugar del esperado 2.68. Esto no es un error: es el resultado del hecho de que la mayoría de las fracciones decimales no se pueden representar exactamente como un flotador. Consulte Aritmética de punto flotante: problemas y limitaciones para obtener más información.

En concreto, esto es un efecto secundario de cómo las computadoras manejan los números de punto flotante en general.

Si necesita más precisión, incluido un redondeo diferente, le sugiero que revise el módulo de Python Decimal . Específicamente de interés, tienen la capacidad de controlar los modos de redondeo . Parece que es posible que desee decimal.ROUND_HALF_DOWN .

Creo que tengo la respuesta a todos los errores de redondeo que la gente ha estado encontrando. He escrito mi propio método, que funciona igual que la “ronda” pero en realidad mira el último dígito y redondea de allí caso por caso. No hay conversión de un decimal a binario. Puede manejar cualquier cantidad de números detrás del decimal y también toma notación científica (como se muestra en los flotadores). ¡También no requiere ninguna importación! ¡Avísame si detectas algún caso que no funcione!

 def Round(Number,digits = 0): Number_Str = str(Number) if "e" in Number_Str: Number_Str = "%.10f" % float(Number_Str) if "." in Number_Str: #If not given an integer try: Number_List = list(Number_Str) #All the characters in Number in a list Number_To_Check = Number_List[Number_List.index(".") + 1 + digits] #Gets value to be looked at for rounding. if int(Number_To_Check) >= 5: Index_Number_To_Round = Number_List.index(".") + digits if Number_List[Index_Number_To_Round] == ".": Index_Number_To_Round -= 1 if int(Number_List[Index_Number_To_Round]) == 9: Number_List_Spliced = Number_List[:Number_List.index(".")+digits] for index in range(-1,-len(Number_List_Spliced) - 1,-1): if Number_List_Spliced[index] == ".": continue elif int(Number_List_Spliced[index]) == 9: Number_List_Spliced[index] = "0" try: Number_List_Spliced[index-1] continue except IndexError: Number_List_Spliced.insert(0,"1") else: Number_List_Spliced[index] = str(int(Number_List_Spliced[index])+1) break FinalNumber = "".join(Number_List_Spliced) else: Number_List[Index_Number_To_Round] = str(int(Number_List[Index_Number_To_Round])+1) FinalNumber = "".join(Number_List[:Index_Number_To_Round + 1]) return float(FinalNumber) else: FinalNumber = "".join(Number_List[:Number_List.index(".") + 1 + digits]) return float(FinalNumber) except IndexError: return float(Number) else: #If given an integer return float(Number) 

Python 3 proporciona métodos de redondeo definidos en el estándar IEEE para la aritmética de punto flotante (IEEE 754) , el redondeo predeterminado [1] se dirige al número más cercano y minimiza los errores acumulativos.

En IEEE 754, hay 5 métodos definidos, dos para redondear al más cercano (Python proporciona el primero por round ) y tres métodos que están dirigidos explícitamente (Python tiene trunc , ceil y floor en su módulo de Math ).

Obviamente necesitas un redondeo dirigido y hay una manera de decirle a este Python, que solo tienes que elegir.


[1] Dado que la representación de los números de punto flotante en las computadoras es limitada, el redondeo no es tan trivial como podrías pensar, ¡te sorprenderá! Recomiendo una lectura cuidadosa de 15. Aritmética de punto flotante: problemas y limitaciones en la documentación de python 3.