round () en Python no parece estar redondeando correctamente

La documentación de la función round () indica que le pasas un número y las posiciones más allá del decimal para redondear. Por eso debería hacer esto:

n = 5.59 round(n, 1) # 5.6 

Pero, en realidad, la vieja rareza de punto flotante se arrastra y se obtiene:

 5.5999999999999996 

Para los propósitos de la interfaz de usuario, necesito mostrar 5.6 . Busqué en Internet y encontré algo de documentación de que esto depende de mi implementación de Python. Desafortunadamente, esto ocurre tanto en mi máquina de desarrollo de Windows como en cada servidor Linux que he probado. Véase aquí también .

Aparte de crear mi propia biblioteca redonda, ¿hay alguna manera de evitar esto?

No puedo evitar la forma en que está almacenado, pero al menos el formato funciona correctamente:

 '%.1f' % round(n, 1) # Gives you '5.6' 

El formateo funciona correctamente incluso sin tener que redondear:

 "%.1f" % n 

Si usa el módulo decimal, puede aproximarse sin usar la función ‘redondear’. Esto es lo que he estado usando para redondear, especialmente al escribir aplicaciones monetarias:

 Decimal(str(16.2)).quantize(Decimal('.01'), rounding=ROUND_UP) 

Esto devolverá un número decimal que es 16.20.

round(5.59, 1) está funcionando bien. El problema es que 5.6 no se puede representar exactamente en punto flotante binario.

 >>> 5.6 5.5999999999999996 >>> 

Como dice Vinko, puede usar el formato de cadena para hacer el redondeo para la visualización.

Python tiene un módulo para la aritmética decimal si lo necesitas.

Obtendrá ‘5.6’ si hace str(round(n, 1)) lugar de solo round(n, 1) .

Puede cambiar el tipo de datos a un entero:

 >>> n = 5.59 >>> int(n * 10) / 10.0 5.5 >>> int(n * 10 + 0.5) 56 

Y luego muestre el número insertando el separador decimal del entorno local.

Sin embargo, la respuesta de Jimmy es mejor.

Las matemáticas de punto flotante son vulnerables a inexactitudes de precisión leves, pero molestas. Si puede trabajar con un número entero o fijo, se le garantizará la precisión.

Echa un vistazo al módulo decimal

Decimal “se basa en un modelo de punto flotante que fue diseñado pensando en las personas, y necesariamente tiene un principio guía primordial: las computadoras deben proporcionar una aritmética que funcione de la misma manera que la aritmética que las personas aprenden en la escuela”. Extracto de La especificación aritmética decimal.

y

Los números decimales se pueden representar exactamente. En contraste, los números como 1.1 y 2.2 no tienen una representación exacta en el punto flotante binario. Los usuarios finales normalmente no esperan que 1.1 + 2.2 se muestren como 3.3000000000000003 como lo hace con un punto flotante binario.

Decimal proporciona el tipo de operaciones que facilitan la escritura de aplicaciones que requieren operaciones de punto flotante y también necesitan presentar esos resultados en un formato legible para el ser humano, por ejemplo, contabilidad.

Es un gran problema por cierto. Prueba este código:

 print "%.2f" % (round((2*4.4+3*5.6+3*4.4)/8,2),) 

Muestra 4.85. Entonces tu haces

 print "Media = %.1f" % (round((2*4.4+3*5.6+3*4.4)/8,1),) 

y muestra 4.8. ¿Los cálculos a mano la respuesta exacta es 4.85, pero si lo intentas?

 print "Media = %.20f" % (round((2*4.4+3*5.6+3*4.4)/8,20),) 

Puedes ver la verdad: el punto flotante se almacena como la sum finita más cercana de las fracciones cuyos denominadores son potencias de dos.

Puede usar el operador de formato de cadena % , similar a sprintf.

 mystring = "%.2f" % 5.5999 

printf el lechón.

 print '%.1f' % 5.59 # returns 5.6 

Funciona perfecto

 format(5.59, '.1f') # to display float(format(5.59, '.1f')) #to round 

Estoy haciendo:

 int(round( x , 0)) 

En este caso, primero redondeamos correctamente en el nivel de la unidad, luego convertimos a entero para evitar imprimir un flotador.

asi que

 >>> int(round(5.59,0)) 6 

Creo que esta respuesta funciona mejor que formando la cadena, y también me da más sentido utilizar la función de redondeo.

Código:

 x1 = 5.63 x2 = 5.65 print(float('%.2f' % round(x1,1))) # gives you '5.6' print(float('%.2f' % round(x2,1))) # gives you '5.7' 

Salida:

 5.6 5.7 

Aquí es donde veo fallar la ronda. ¿Qué pasaría si quisieras redondear estos 2 números a un decimal? 23.45 23.55 Mi educación fue que al redondear estos debería obtener: 23.4 23.6 la “regla” es que debe redondear hacia arriba si el número anterior era impar, no redondear hacia arriba si el número anterior era par. La función redonda en python simplemente trunca el 5.

El problema es solo cuando el último dígito es 5. Ej. 0.045 se almacena internamente como 0.044999999999999 … Simplemente puede incrementar el último dígito a 6 y redondear. Esto le dará los resultados deseados.

 import re def custom_round(num, precision=0): # Get the type of given number type_num = type(num) # If the given type is not a valid number type, raise TypeError if type_num not in [int, float, Decimal]: raise TypeError("type {} doesn't define __round__ method".format(type_num.__name__)) # If passed number is int, there is no rounding off. if type_num == int: return num # Convert number to string. str_num = str(num).lower() # We will remove negative context from the number and add it back in the end negative_number = False if num < 0: negative_number = True str_num = str_num[1:] # If number is in format 1e-12 or 2e+13, we have to convert it to # to a string in standard decimal notation. if 'e-' in str_num: # For 1.23e-7, e_power = 7 e_power = int(re.findall('e-[0-9]+', str_num)[0][2:]) # For 1.23e-7, number = 123 number = ''.join(str_num.split('e-')[0].split('.')) zeros = '' # Number of zeros = e_power - 1 = 6 for i in range(e_power - 1): zeros = zeros + '0' # Scientific notation 1.23e-7 in regular decimal = 0.000000123 str_num = '0.' + zeros + number if 'e+' in str_num: # For 1.23e+7, e_power = 7 e_power = int(re.findall('e\+[0-9]+', str_num)[0][2:]) # For 1.23e+7, number_characteristic = 1 # characteristic is number left of decimal point. number_characteristic = str_num.split('e+')[0].split('.')[0] # For 1.23e+7, number_mantissa = 23 # mantissa is number right of decimal point. number_mantissa = str_num.split('e+')[0].split('.')[1] # For 1.23e+7, number = 123 number = number_characteristic + number_mantissa zeros = '' # Eg: for this condition = 1.23e+7 if e_power >= len(number_mantissa): # Number of zeros = e_power - mantissa length = 5 for i in range(e_power - len(number_mantissa)): zeros = zeros + '0' # Scientific notation 1.23e+7 in regular decimal = 12300000.0 str_num = number + zeros + '.0' # Eg: for this condition = 1.23e+1 if e_power < len(number_mantissa): # In this case, we only need to shift the decimal e_power digits to the right # So we just copy the digits from mantissa to characteristic and then remove # them from mantissa. for i in range(e_power): number_characteristic = number_characteristic + number_mantissa[i] number_mantissa = number_mantissa[i:] # Scientific notation 1.23e+1 in regular decimal = 12.3 str_num = number_characteristic + '.' + number_mantissa # characteristic is number left of decimal point. characteristic_part = str_num.split('.')[0] # mantissa is number right of decimal point. mantissa_part = str_num.split('.')[1] # If number is supposed to be rounded to whole number, # check first decimal digit. If more than 5, return # characteristic + 1 else return characteristic if precision == 0: if mantissa_part and int(mantissa_part[0]) >= 5: return type_num(int(characteristic_part) + 1) return type_num(characteristic_part) # Get the precision of the given number. num_precision = len(mantissa_part) # Rounding off is done only if number precision is # greater than requested precision if num_precision <= precision: return num # Replace the last '5' with 6 so that rounding off returns desired results if str_num[-1] == '5': str_num = re.sub('5$', '6', str_num) result = round(type_num(str_num), precision) # If the number was negative, add negative context back if negative_number: result = result * -1 return result 

Qué pasa:

 round(n,1)+epsilon