Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

force non-exponential representation for Decimal?

23 views
Skip to first unread message

jh...@gmx.de

unread,
Dec 23, 2009, 3:24:40 AM12/23/09
to pytho...@python.org
(re-posting this because of missing subject - sorry for the hassle)

Hi,

I need to convert Python decimal.Decimal data to the XMLSchema xs:decimal datatype. This is reasonably straightforward, but there are some corner cases.

In particular, xs:decimal does not allow exponential notation like:

>>> print Decimal('0.00000000000000000023430000000837483727772')
2.3430000000837483727772E-19
>>>

Is there a convenient way to force a decimal.Decimal representation to not use exponential representation?

While I've seen this decimal FAQ entry:

Q. Some decimal values always print with exponential notation. Is there a way to get a non-exponential representation?

A. For some values, exponential notation is the only way to express the number of significant places in the coefficient. For example, expressing 5.0E+3 as 5000 keeps the value constant but cannot show the original’s two-place significance.

If an application does not care about tracking significance, it is easy to remove the exponent and trailing zeroes, losing significance, but keeping the value unchanged:

>>> def remove_exponent(d):
... return d.quantize(Decimal(1)) if d == d.to_integral() else d.normalize()

>>> remove_exponent(Decimal('5E+3'))
Decimal('5000')

...this doesn't really apply to my usecase:

It does not work for "small" values:

remove_exponent(Decimal('0.00000000000000000023430000000837483727772'))
Decimal('2.3430000000837483727772E-19')
>>>

and it can lead to errors if the sheer number size can't be handled:

>>> d2 = Decimal('1e80')
>>> remove_exponent(d2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in remove_exponent
File "/apps/prod/gcc/4.2.1/lib/python2.6/decimal.py", line 2308, in quantize
'quantize result has too many digits for current context')
File "/apps/prod/gcc/4.2.1/lib/python2.6/decimal.py", line 3680, in _raise_error
raise error(explanation)
decimal.InvalidOperation: quantize result has too many digits for current context
>>>

I could easily adapt this recipe:

http://code.activestate.com/recipes/358361/

which works on the string and basically removes exponential notation, moves the fraction '.'-dot and adds appropriate zeros.

Doesn't seem very lightweight, though.

Any obvious alternatives I'm missing?

Thanks,
Holger

--
Jetzt kostenlos herunterladen: Internet Explorer 8 und Mozilla Firefox 3.5 -
sicherer, schneller und einfacher! http://portal.gmx.net/de/go/chbrowser

Peter Otten

unread,
Dec 23, 2009, 4:28:40 AM12/23/09
to
jh...@gmx.de wrote:

> I need to convert Python decimal.Decimal data to the XMLSchema xs:decimal
> datatype. This is reasonably straightforward, but there are some corner
> cases.
>
> In particular, xs:decimal does not allow exponential notation like:
>
>>>> print Decimal('0.00000000000000000023430000000837483727772')
> 2.3430000000837483727772E-19
>>>>
>
> Is there a convenient way to force a decimal.Decimal representation to not
> use exponential representation?

> Any obvious alternatives I'm missing?

Decimal numbers seem to support new-style formatting:

>>> from decimal import Decimal
>>> d = Decimal('2.3430000000837483727772E-19')
>>> format(d, "f")
'0.00000000000000000023430000000837483727772'

Peter

jh...@gmx.de

unread,
Dec 23, 2009, 5:03:58 AM12/23/09
to Mark Dickinson, pytho...@python.org
(cc-ing the list)

> > Is there a convenient way to force a decimal.Decimal representation to
> not use exponential representation?
>

> Which Python version are you using? For Python 2.6 (and 3.1), the
> answer's yes. For earlier Python verions, I don't think so. In
> Python 2.6, use new-style formatting with the 'f' modifier:
>
> >>> '{0:f}'.format(Decimal('1e-100'))
> '0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001'
> >>> '{0:f}'.format(Decimal('1e100'))
> '10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'

Unfortunately, I'm still using Python 2.4 so I can't go that way (at least not anytime soon, this is a production environment).

> For earlier Python versions, you can manipulate the output string as
> you describe, or you can extract the raw components of the Decimal
> instance (e.g. with the as_tuple() method) and construct a string
> directly from those. You might be also be able to extract code for
> the '{:f}' formatting from the Python 2.6 Decimal source (Lib/
> decimal.py), but it's fairly convoluted.

Thanks a lot for the hints, I'll look into that.

Holger

--
GRATIS f�r alle GMX-Mitglieder: Die maxdome Movie-FLAT!
Jetzt freischalten unter http://portal.gmx.net/de/go/maxdome01

Mark Dickinson

unread,
Dec 23, 2009, 5:16:27 AM12/23/09
to
On Dec 23, 10:03 am, jh...@gmx.de wrote:
> (cc-ing the list)

Thanks. Looks like I'm still having trouble distinguishing between
'Reply' and 'Reply to author'. I'll have to work on my reading
abilities over the break...

> > > Is there a convenient way to force a decimal.Decimal representation to
> > not use exponential representation?
>
> > Which Python version are you using?  For Python 2.6 (and 3.1), the
> > answer's yes.  For earlier Python verions, I don't think so.  In
> > Python 2.6, use new-style formatting with the 'f' modifier:
>
> > >>> '{0:f}'.format(Decimal('1e-100'))
> > '0.000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000001'
> > >>> '{0:f}'.format(Decimal('1e100'))
> > '10000000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000000'
>
> Unfortunately, I'm still using Python 2.4 so I can't go that way (at least not anytime soon, this is a production environment).

Hmm. The recipe you cite is probably the easiest option, then.

I can't help wondering what you're doing with numbers that small.
2.34e-19 looks an awful lot like 0 for many practical purposes...

--
Mark

jh...@gmx.de

unread,
Dec 23, 2009, 7:52:14 AM12/23/09
to pytho...@python.org, Mark Dickinson
-- sorry if this comes out-of-thread-line but I forgot to enable mailing list delivery :-( --

> I can't help wondering what you're doing with numbers that small.
> 2.34e-19 looks an awful lot like 0 for many practical purposes...

Just an arbitrary example to show the behaviour.

As I don't have control over the data I cannot be sure to never get numbers that are represented in exponential notation per default by Decimal (if these numbers are sensible is another story). So to be on the safe side and not break any XML Schema validation I want to enforce XML-correct decimal formatting.

Thanks again,
Holger
--
Preisknaller: GMX DSL Flatrate f�r nur 16,99 Euro/mtl.!
http://portal.gmx.net/de/go/dsl02

Mensanator

unread,
Dec 23, 2009, 4:06:40 PM12/23/09
to
On Dec 23, 4:03 am, jh...@gmx.de wrote:
> (cc-ing the list)
>
> > > Is there a convenient way to force a decimal.Decimal representation to
> > not use exponential representation?
>
> > Which Python version are you using?  For Python 2.6 (and 3.1), the
> > answer's yes.  For earlier Python verions, I don't think so.  In
> > Python 2.6, use new-style formatting with the 'f' modifier:
>
> > >>> '{0:f}'.format(Decimal('1e-100'))
> > '0.000000000000000000000000000000000000000000000000000000000000000000000000­0000000000000000000000000001'

> > >>> '{0:f}'.format(Decimal('1e100'))
> > '10000000000000000000000000000000000000000000000000000000000000000000000000­000000000000000000000000000'

>
> Unfortunately, I'm still using Python 2.4 so I can't go that way (at least not anytime soon, this is a production environment).

But you can get gmpy for Python 2.4. And then you can do:

>>> c = gmpy.mpf('2.3430000000837483727772E-19')
>>> c
mpf('2.34300000008374837278e-19')

>>> help(gmpy.fdigits)

Help on built-in function fdigits in module gmpy:

fdigits(...)
fdigits(x, base=10, digs=0, mine=0, maxe=-1, opts=0): formats x,
which is an mpf or else gets coerced to one.

Returns up to digs digits in the given base (if digs is 0, as many
digits as are available), but no more than available given x's
precision; the resulting string is formatted in fixed point
if the exponent is >=mine and <=maxe, else in exponential (the
exponent-separator is 'e' for base up to 10, else '@' -- the
exponent is always output as a signed, base-10 integer). If opts
has bit 1 set, the whole is wrapped in 'gmpy.mpf(...)', to ease
later approximate reconstruction via builtin function eval
(Or, in just mpf(...) if gmpy.set_tagoff(1) was called).

If opts has bit 2 set, then opts bit 1, mine, and maxe, are
ignored; the result is then a 2-element tuple, first element
the raw string of base-digits without formatting, second the
exponent in base as a Python int.

>>> gmpy.fdigits(c,10,30,-30,30)
'0.000000000000000000234300000008374837278'


>
> > For earlier Python versions, you can manipulate the output string as
> > you describe, or you can extract the raw components of the Decimal
> > instance (e.g. with the as_tuple() method) and construct a string
> > directly from those.  You might be also be able to extract code for
> > the '{:f}' formatting from the Python 2.6 Decimal source (Lib/
> > decimal.py), but it's fairly convoluted.
>
> Thanks a lot for the hints, I'll look into that.
>
> Holger
>
> --

> GRATIS für alle GMX-Mitglieder: Die maxdome Movie-FLAT!
> Jetzt freischalten unterhttp://portal.gmx.net/de/go/maxdome01

0 new messages