2012-09-07 21 views
9

Estoy tratando de imprimir un número en formato de ingeniería con Python, pero parece que no puedo hacerlo funcionar. La sintaxis PARECE bastante simple, pero simplemente no funciona.Número de impresión en formato de ingeniería

>>> import decimal 
>>> x = decimal.Decimal(1000000) 
>>> print x 
1000000 
>>>> print x.to_eng_string() 
1000000 

No puedo entender por qué es esto. Los dos valores no son iguales (uno es una cadena, el otro es un int). Establecer varios contextos en decimal tampoco parece ayudar. ¿Alguna pista o idea?

+0

he encontrado la solución aquí ".4G%" % x – lehalle

Respuesta

17

Para conseguir que esto funcione, hay que normalizar la decimal primera:

>>> x = decimal.Decimal ('10000000') 

>>> x.normalize() 
Decimal('1E+7') 

>>> x.normalize().to_eng_string() 
'10E+6' 

La razón de esto puede ser descubierto por ahondar en la fuente código.

Si examina to_eng_string() en el Python 2.7.3 árbol de fuentes (Lib/decimal.py de la bola de alquitrán fuente gzipped here), que simplemente llama __str__ con eng establece en true.

A continuación, puede ver que se decide sobre cuántos dígitos ir a la izquierda del punto decimal inicialmente con:

leftdigits = self._exp + len(self._int) 

La siguiente tabla muestra cuáles son los valores de estas dos cosas:

      ._exp  ._int   len leftdigits 
         -----  ---------  --- ---------- 
Decimal (1000000)   0  '1000000'  7   7 
Decimal ('1E+6')    6  '1'    1   7 

El código que sigue después de eso es:

if self._exp <= 0 and leftdigits > -6: 
    # no exponent required 
    dotplace = leftdigits 
elif not eng: 
    # usual scientific notation: 1 digit on left of the point 
    dotplace = 1 
elif self._int == '0': 
    # engineering notation, zero 
    dotplace = (leftdigits + 1) % 3 - 1 
else: 
    # engineering notation, nonzero 
    dotplace = (leftdigits - 1) % 3 + 1 

y se puede ver que, unle Si ya es tiene un exponente en un cierto rango (self._exp > 0 or leftdigits <= -6), no se le dará ninguno en la representación de cadena.


La investigación adicional muestra el motivo de este comportamiento. Al mirar el código en sí, verá que está basado en el General Decimal Arithmetic Specification (PDF here).

Si usted busca en ese documento para to-scientific-string (sobre la que se basa en gran medida to-engineering-string), se establece en la parte (parafraseado, y con los pedazos en negrilla):

Las "a-científica-string" convertidos operación un número a una cadena, usando la notación científica si se necesita un exponente. La operación no se ve afectada por el contexto.

Si el número es un número finito entonces:

El coeficiente se convierte primero en una cadena en base diez utilizando los caracteres 0 a 9 sin ceros a la izquierda (excepto si su valor es cero, en la que un caso se usa un solo carácter 0).

A continuación, se calcula el exponente ajustado; este es el exponente, más el número de caracteres en el coeficiente convertido, menos uno. Es decir, exponente + (clength-1), donde clength es la longitud del coeficiente en dígitos decimales.

Si el exponente es menor o igual que cero y el exponente ajustado es mayor o igual que -6, el número se convertirá a una forma de carácter sin usar la notación exponencial. En este caso, si el exponente es cero, entonces no se agrega punto decimal. De lo contrario (el exponente será negativo), se insertará un punto decimal con el valor absoluto del exponente que especifica el número de caracteres a la derecha del punto decimal. Los caracteres "0" se agregan a la izquierda del coeficiente convertido según sea necesario. Si ningún carácter precede al punto decimal después de esta inserción, entonces se prefija un carácter "0" convencional.

En otras palabras, está haciendo lo que está haciendo porque eso es lo que el estándar le dice que haga.

+0

¿Por qué molestarse en verificar eso? Le pedí algo específico. Entonces decidió que no quería eso y quería lo que le di. Python me confunde a veces ... – jmurrayufo

+2

@jmurrayufo, mira la actualización. Básicamente, está haciendo eso porque eso es lo que el estándar le dice que haga. – paxdiablo

4

Me parece que vas a tener que rodar su propia:

from math import log10 
def eng_str(x): 
    y = abs(x) 
    exponent = int(log10(y)) 
    engr_exponent = exponent - exponent%3 
    z = y/10**engr_exponent 
    sign = '-' if x < 0 else '' 
    return sign+str(z)+'e'+str(engr_exponent) 

Aunque es posible que desee tomar un poco más de cuidado en el formato de la porción z ...

no bien probado. No dude en editar si encuentra errores

+0

Si bien no me importa la idea, estoy más curioso por qué el 'decimal' clase no hace lo que parece que debería, o cómo lo uso mal – jmurrayufo

+0

@jmurrayufo - Lo siento, supongo que entendí mal la pregunta :). – mgilson

-1

Anteriormente traté de responder, pero descubrí que sigue siendo la notación científica. La notación de ingeniería se admitirá en las versiones futuras del especificador de formato Python (3.2?), Pero no en Python 2.7.

examinar esta cuestión http://bugs.python.org/issue8060

+1

En realidad, eso está relacionado pero es para tipos de EFloat en lugar de Decimal. – paxdiablo

+0

La interpretación de paxdiablo parece incorrecta, el EFloat mencionado parece ser solo una clase de demostración para el comportamiento deseado. Pero este problema de 2010 se ha cerrado/rechazado recientemente. – handle

2

Soy consciente de que este es un viejo hilo, pero viene cerca de la parte superior de una búsqueda de python engineering notation.

Soy un ingeniero al que le gustan las unidades de ingeniería "ingeniería 101". Ni siquiera me gustan las designaciones como 0.1uF, quiero que lea 100nF. Jugué con la clase Decimal y realmente no me gustó su comportamiento en el rango de valores posibles, así que lancé un paquete llamado engineering_notation que es instalable por pip.

pip install engineering_notation 

Desde dentro de Python:

>>> from engineering_notation import EngNumber 
>>> EngNumber('1000000') 
1M 
>>> EngNumber(1000000) 
1M 
>>> EngNumber(1000000.0) 
1M 
>>> EngNumber('0.1u') 
100n 
>>> EngNumber('1000m') 
1 

Este paquete también es compatible con las comparaciones y otras operaciones numéricas simples.

https://github.com/slightlynybbled/engineering_notation

Cuestiones relacionadas