2012-01-15 40 views
21

En mi programa, la precisión decimal es muy importante.
Muchos de mis cálculos deben ser precisos para muchos decimales (como 50).Problemas con el redondeo de decimales (python)

porque estoy utilizando pitón, He estado usando el módulo decimal (con contexto() prec = 99. es decir;. Set tener 99 cifras decimales de precisión cuando instanciar un objeto decimal)
como Pythonic los flotadores no permiten cerca de tal precisión.

Como deseo que el Usuario especifique los decimales de precisión de los cálculos, he tenido que implementar varias funciones de redondeo() en mi código.
Desafortunadamente, la función redonda incorporada y el objeto decimal no interactúan bien.

round(decimal.Decimal('2.000000000000000000001'),50) 

# Number is 1e-21. There are 21 decimal places, much less than 50. 

Sin embargo, el resultado es 2.0 en lugar de 2,000000000000000000001
La función de redondeo no es el redondeo a 50. Mucho menos!

Sí, me he asegurado de que el exceso de redondeo no ocurra en la creación de instancias del objeto Decimal, pero después de realizar una llamada.
Siempre paso cadenas que representan flotadores en el constructor de decimales, nunca flotantes pitonicas.

¿Por qué la función circular me está haciendo esto?
(me di cuenta de que probablemente fue diseñado originalmente para los flotadores Pythonic que nunca puede tener tantas cifras decimales, pero la documentación afirma que el objeto decimal se integra perfectamente en el código Python y es compatible con las funciones de Python incorporadas!)

¡Gracias profusamente!
(Esto me tiene muy nerviosa, ya que este problema socava el uso de todo el programa)

Especificaciones:
Python 2.7.1
Windows 7
módulo decimal (incorporado)

+0

Quiso decir que restar 2 después de redondeo? – celtschk

+0

Disculpa, fue un error tipográfico –

+0

¿No estás seguro de cuál es la respuesta correcta? Aroth respondió mi pregunta, ¡pero Raymond brindó la solución! –

Respuesta

7

El problema, como lo que puedo determinar, es que round() está volviendo un tipo Python float, y no un tipo Decimal. Por lo tanto, no importa la precisión que establezca en el módulo decimal, porque una vez que llame al round() ya no tendrá un Decimal.

Para solucionar esto, probablemente tendrá que encontrar una forma alternativa de redondear sus números que no dependa de round(). Tal como la sugerencia de Raymond.

Usted puede encontrar este ejemplo Ideone corta ilustrativa: http://ideone.com/xgPL9

+0

¡Por supuesto! Acabo de encontrar este fragmento de la documentación también ... ronda (a, 1) # round() primero se convierte en punto flotante binario –

39

Desde round() coacciona su entrada a un flotador binario regular, la forma preferida de redondear objetos decimales es con el método quantize():

>>> from decimal import Decimal 
>>> d = Decimal('2.000000000000000000001') 

>>> d.quantize(Decimal(10) ** -20)  # Round to twenty decimal places 
Decimal('2.00000000000000000000') 

a CHOP-off los ceros de salida, aplicar normalize() al resultado redondeado:

>>> d = Decimal('2.000000000000000000001') 
>>> d.quantize(Decimal(10) ** -20).normalize() 
Decimal('2') 
+0

Miré esta función, pero devuelve yuck los 0 finales cuando redondeas a más decimales que el decimal (las cifras significativas, lo cual es sensato). La documentación para el decimal dice que hay dos operaciones en cuantizar; el redondeo y luego las cifras significativas (o eso he interpretado). ¿Hay alguna manera de simplemente realizar el redondeo? (De lo contrario, me limitaré a manipular en cadena los sig-higos, supongo. –

+0

Utilice el formato de cadena para controlar la apariencia del decimal: '' formato (Decimal ('2.00000000000000000000'), '.1f') ''imprime en uno lugar decimal. También puede convertirlo en una cadena y usar * rstrip *: '' str (Decimal ('2.00000000000000000000')). rstrip ('0') ''. –

+0

Eliminar los 0s posteriores con 'Decimal ('2.00000000000000000000') .normalize() '. – unutbu

3

Usando round() para cualquier propósito tiene dificultades:

(1) En Python 2.7, redondo (decimal ('39 .142')) produce 39,14 es decir, un flotador; 3.2 produce un decimal.

>>> import decimal; round(decimal.Decimal('2.000000000000000000001'),50) 
2.0 <<<=== ***float*** 

(2) round() tiene solo un modo de redondeo; Decimal.quantize tiene muchos.

(3) Para todos los tipos de entrada, el único modo de redondeo cambió: Python 2.7 redondea desde 0 (esperado en la mayoría de las aplicaciones comerciales) mientras que Python 3.2 redondea al múltiplo par más cercano de 10 ** - n (menos esperado en negocio).

Uso Decimal.quantize()

4

Pruebe lo siguiente ...

from decimal import Decimal, ROUND_HALF_UP, getcontext 

getcontext().prec = 51 

def round_as_decimal(num, decimal_places=2): 
    """Round a number to a given precision and return as a Decimal 

    Arguments: 
    :param num: number 
    :type num: int, float, decimal, or str 
    :returns: Rounded Decimal 
    :rtype: decimal.Decimal 
    """ 
    precision = '1.{places}'.format(places='0' * decimal_places) 
    return Decimal(str(num)).quantize(Decimal(precision), rounding=ROUND_HALF_UP) 

round_as_decimal('2.000000000000000000001', decimal_places=50) 

Espero que ayude!

2

Como se indicó anteriormente, round() devuelve un flotante si se proporciona con un decimal. Para los cálculos exactos, le sugiero que ver con no redondean hacia abajo decimales, y para redondear el resultado final, utilizar una función:

def dround(decimal_number, decimal_places): 
     return decimal_number.quantize(Decimal(10) ** -decimal_places) 
Cuestiones relacionadas