Floating point "g" format not stripping trailing zeros

classic Classic list List threaded Threaded
21 messages Options
12
Reply | Threaded
Open this post in threaded view
|

Floating point "g" format not stripping trailing zeros

Hrvoje Nikšić-3
According to the documentation of the "g" floating-point format,
trailing zeros should be stripped from the resulting string:

"""
General format. For a given precision p >= 1, this rounds the number
to p significant digits and then formats the result in either
fixed-point format or in scientific notation, depending on its
magnitude.[...]
In both cases insignificant trailing zeros are removed from the
significand, and the decimal point is also removed if there are no
remaining digits following it.
"""

However, in some cases, the trailing zeros apparently remain:

>>> from decimal import Decimal as D
>>> x = D(1)/D(999)
>>> '{:.15g}'.format(x)
'0.00100100100100100'

For floats, the trailing zeros are removed:

>>> '{:.15g}'.format(1. / 999)
'0.001001001001001'

This behavior is present in both 2.7.8 and 3.4.1. Is this a bug in the
formatting of Decimals?


Reply | Threaded
Open this post in threaded view
|

Floating point "g" format not stripping trailing zeros

Mark Lawrence
On 11/02/2015 20:02, Hrvoje Nik?i? wrote:

> According to the documentation of the "g" floating-point format,
> trailing zeros should be stripped from the resulting string:
>
> """
> General format. For a given precision p >= 1, this rounds the number
> to p significant digits and then formats the result in either
> fixed-point format or in scientific notation, depending on its
> magnitude.[...]
> In both cases insignificant trailing zeros are removed from the
> significand, and the decimal point is also removed if there are no
> remaining digits following it.
> """
>
> However, in some cases, the trailing zeros apparently remain:
>
>>>> from decimal import Decimal as D
>>>> x = D(1)/D(999)
>>>> '{:.15g}'.format(x)
> '0.00100100100100100'
>
> For floats, the trailing zeros are removed:
>
>>>> '{:.15g}'.format(1. / 999)
> '0.001001001001001'
>
> This behavior is present in both 2.7.8 and 3.4.1. Is this a bug in the
> formatting of Decimals?
>

I'd say it's a bug.  P is 15, you've got 17 digits after the decimal
place and two of those are insignificant trailing zeros.

--
My fellow Pythonistas, ask not what our language can do for you, ask
what you can do for our language.

Mark Lawrence



Reply | Threaded
Open this post in threaded view
|

Floating point "g" format not stripping trailing zeros

Ian Kelly-2
On Wed, Feb 11, 2015 at 2:48 PM, Mark Lawrence <breamoreboy at yahoo.co.uk> wrote:

> On 11/02/2015 20:02, Hrvoje Nik?i? wrote:
>>
>> According to the documentation of the "g" floating-point format,
>> trailing zeros should be stripped from the resulting string:
>>
>> """
>> General format. For a given precision p >= 1, this rounds the number
>> to p significant digits and then formats the result in either
>> fixed-point format or in scientific notation, depending on its
>> magnitude.[...]
>> In both cases insignificant trailing zeros are removed from the
>> significand, and the decimal point is also removed if there are no
>> remaining digits following it.
>> """
>>
>> However, in some cases, the trailing zeros apparently remain:
>>
>>>>> from decimal import Decimal as D
>>>>> x = D(1)/D(999)
>>>>> '{:.15g}'.format(x)
>>
>> '0.00100100100100100'
>>
>> For floats, the trailing zeros are removed:
>>
>>>>> '{:.15g}'.format(1. / 999)
>>
>> '0.001001001001001'
>>
>> This behavior is present in both 2.7.8 and 3.4.1. Is this a bug in the
>> formatting of Decimals?
>>
>
> I'd say it's a bug.  P is 15, you've got 17 digits after the decimal place
> and two of those are insignificant trailing zeros.

Actually it's the float version that doesn't match the documentation.
In the decimal version, sure there are 17 digits after the decimal
place there, but the first two -- which are leading zeroes -- would
not normally be considered significant. The float version OTOH is only
giving you 13 significant digits when 15 were requested.

For illustration, compare the same formatting with the scale ramped up
to make it switch to exponential notation:

>>> '{:.15g}'.format(D(1) / D(999000000))
'1.00100100100100e-9'
>>> '{:.15g}'.format(1./999000000)
'1.001001001001e-09'


Reply | Threaded
Open this post in threaded view
|

Floating point "g" format not stripping trailing zeros

Ian Kelly-2
On Wed, Feb 11, 2015 at 3:19 PM, Ian Kelly <ian.g.kelly at gmail.com> wrote:

> On Wed, Feb 11, 2015 at 2:48 PM, Mark Lawrence <breamoreboy at yahoo.co.uk> wrote:
>> On 11/02/2015 20:02, Hrvoje Nik?i? wrote:
>>>
>>> According to the documentation of the "g" floating-point format,
>>> trailing zeros should be stripped from the resulting string:
>>>
>>> """
>>> General format. For a given precision p >= 1, this rounds the number
>>> to p significant digits and then formats the result in either
>>> fixed-point format or in scientific notation, depending on its
>>> magnitude.[...]
>>> In both cases insignificant trailing zeros are removed from the
>>> significand, and the decimal point is also removed if there are no
>>> remaining digits following it.
>>> """
>>>
>>> However, in some cases, the trailing zeros apparently remain:
>>>
>>>>>> from decimal import Decimal as D
>>>>>> x = D(1)/D(999)
>>>>>> '{:.15g}'.format(x)
>>>
>>> '0.00100100100100100'
>>>
>>> For floats, the trailing zeros are removed:
>>>
>>>>>> '{:.15g}'.format(1. / 999)
>>>
>>> '0.001001001001001'
>>>
>>> This behavior is present in both 2.7.8 and 3.4.1. Is this a bug in the
>>> formatting of Decimals?
>>>
>>
>> I'd say it's a bug.  P is 15, you've got 17 digits after the decimal place
>> and two of those are insignificant trailing zeros.
>
> Actually it's the float version that doesn't match the documentation.
> In the decimal version, sure there are 17 digits after the decimal
> place there, but the first two -- which are leading zeroes -- would
> not normally be considered significant. The float version OTOH is only
> giving you 13 significant digits when 15 were requested.
>
> For illustration, compare the same formatting with the scale ramped up
> to make it switch to exponential notation:
>
>>>> '{:.15g}'.format(D(1) / D(999000000))
> '1.00100100100100e-9'
>>>> '{:.15g}'.format(1./999000000)
> '1.001001001001e-09'

Although here's something else that is definitely an issue with the
decimal formatting:

>>> '{:.15g}'.format(D(1)/D(999000))
'0.00000100100100100100'

This has an exponent of -6, so according to the docs it should be
formatted in exponential notation.


Reply | Threaded
Open this post in threaded view
|

Floating point "g" format not stripping trailing zeros

Hrvoje Nikšić-3
In reply to this post by Hrvoje Nikšić-3
> >>>>> from decimal import Decimal as D
> >>>>> x = D(1)/D(999)
> >>>>> '{:.15g}'.format(x)
> >>
> >> '0.00100100100100100'
[...]
> > I'd say it's a bug.  P is 15, you've got 17 digits after the decimal place
> > and two of those are insignificant trailing zeros.
>
> Actually it's the float version that doesn't match the documentation.
> In the decimal version, sure there are 17 digits after the decimal
> place there, but the first two -- which are leading zeroes -- would
> not normally be considered significant.

{:.15g} is supposed to give 15 digits of precision, but with trailing
zeros removed.  For example, '{:.15g}'.format(Decimal('0.5')) should
yield '0.5', not '0.500000000000000' -- and, it indeed does.  It is
only for some numbers that trailing zeros are not removed, which looks
like a bug.  The behavior of floats matches both the documentation and
other languages using the 'g' decimal format, such as C.

> The float version OTOH is only giving you 13 significant digits when
> 15 were requested.

It is giving 15 significant digits if you count the trailing zeros
that have been removed.  If those two digits had not been zeros, they
would have been included.  This is again analogous to
'{:.15g}'.format(0.5) returning '0.5'.


Reply | Threaded
Open this post in threaded view
|

Floating point "g" format not stripping trailing zeros

Ian Kelly-2
On Thu, Feb 12, 2015 at 1:23 PM, Hrvoje Nik?i? <hniksic at gmail.com> wrote:

>> >>>>> from decimal import Decimal as D
>> >>>>> x = D(1)/D(999)
>> >>>>> '{:.15g}'.format(x)
>> >>
>> >> '0.00100100100100100'
> [...]
>> > I'd say it's a bug.  P is 15, you've got 17 digits after the decimal place
>> > and two of those are insignificant trailing zeros.
>>
>> Actually it's the float version that doesn't match the documentation.
>> In the decimal version, sure there are 17 digits after the decimal
>> place there, but the first two -- which are leading zeroes -- would
>> not normally be considered significant.
>
> {:.15g} is supposed to give 15 digits of precision, but with trailing
> zeros removed.

The doc says with "insignificant" trailing zeros removed, not all
trailing zeros.

> For example, '{:.15g}'.format(Decimal('0.5')) should
> yield '0.5', not '0.500000000000000' -- and, it indeed does.  It is
> only for some numbers that trailing zeros are not removed, which looks
> like a bug.  The behavior of floats matches both the documentation and
> other languages using the 'g' decimal format, such as C.

Ah, I see now what's going on here. With floats, there is really no
notion of significant digits. The values 0.5 and 0.50000 are
completely equivalent. With decimals, that's not exactly true; if you
give the decimal a trailing zero then you are telling it that the zero
is significant.

>>> Decimal('0.5')
Decimal('0.5')
>>> Decimal('0.50000')
Decimal('0.50000')
>>> Decimal('0.5').as_tuple()
DecimalTuple(sign=0, digits=(5,), exponent=-1)
>>> Decimal('0.50000').as_tuple()
DecimalTuple(sign=0, digits=(5, 0, 0, 0, 0), exponent=-5)

These are distinct; the decimal knows how many significant digits you
passed it. As a result, these are also distinct:

>>> '{:.4g}'.format(Decimal('0.5'))
'0.5'
>>> '{:.4g}'.format(Decimal('0.50000'))
'0.5000'

Now what happens in your original example of 1/999? The default
decimal context uses 28 digits of precision, so the result of that
calculation will have 28 significant digits in it.

>>> decimal.getcontext().prec
28
>>> Decimal(1) / Decimal(999)
Decimal('0.001001001001001001001001001001')

When you specify the a precision of 15 in your format string, you're
telling it to take the first 15 of those. It doesn't care that the
last couple of those are zeros, because as far as it's concerned,
those digits are significant.


Reply | Threaded
Open this post in threaded view
|

Floating point "g" format not stripping trailing zeros

Hrvoje Nikšić-3
In reply to this post by Hrvoje Nikšić-3
Ian Kelly writes:
> When you specify the a precision of 15 in your format string, you're
> telling it to take the first 15 of those. It doesn't care that the
> last couple of those are zeros, because as far as it's concerned,
> those digits are significant.

OK, it's a bit surprising, but also consistent with the rest of the
decimal module. Thanks for clearing it up.

My concrete use case is printing an arbitrary fraction as a
user-readable decimal, rounded to the specified number of digits, and
using the exponential notation where appropriate:

import decimal
_dec_fmt_context = decimal.Context(prec=15, rounding=decimal.ROUND_HALF_UP)
def _format(frac):
    with decimal.localcontext(_dec_fmt_context):
        dec = decimal.Decimal(frac.numerator) /
decimal.Decimal(frac.denominator)
        return '{:g}'.format(dec)

The decimal obtained by dividing the numerator with the denominator
includes trailing zeros. Calling normalize() to get rid of them will
have the unfortunate side effect of turning 9806650 into 9.80665e+6,
and the method recommended in the documentation:

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

...will raise "decimal.InvalidOperation: quantize result has too many
digits for current context" when the number is too large. For now I'm
emulating the behavior of '%g' on floats using rstrip('0') to get rid
of the trailing zeros:

    ...
        s = '{:g}'.format(dec)
    if '.' in s and 'e' not in s:
        s = s.rstrip('0')
        s = s.rstrip('.')
    return s


Reply | Threaded
Open this post in threaded view
|

Floating point "g" format not stripping trailing zeros

Grant Edwards-7
In reply to this post by Hrvoje Nikšić-3
On 2015-02-12, Ian Kelly <ian.g.kelly at gmail.com> wrote:
> On Thu, Feb 12, 2015 at 1:23 PM, Hrvoje Nik?i? <hniksic at gmail.com> wrote:
>
>> {:.15g} is supposed to give 15 digits of precision, but with trailing
>> zeros removed.
>
> The doc says with "insignificant" trailing zeros removed, not all
> trailing zeros.

Can somebody explain the difference between "significant" and
"insignificant" tailing zeros to somebody who barely passed his single
numerical methods class?  [Though I have, on occasion, had to tinker
with the innards of SW floating point libraries and could fish a
hardcopy of IEEE-754 out of a filing cabinet if needed.]

--
Grant Edwards               grant.b.edwards        Yow! My polyvinyl cowboy
                                  at               wallet was made in Hong
                              gmail.com            Kong by Montgomery Clift!


Reply | Threaded
Open this post in threaded view
|

Floating point "g" format not stripping trailing zeros

Ian Kelly-2
In reply to this post by Hrvoje Nikšić-3
On Fri, Feb 13, 2015 at 2:26 AM, Hrvoje Nik?i? <hniksic at gmail.com> wrote:

> Ian Kelly writes:
>> When you specify the a precision of 15 in your format string, you're
>> telling it to take the first 15 of those. It doesn't care that the
>> last couple of those are zeros, because as far as it's concerned,
>> those digits are significant.
>
> OK, it's a bit surprising, but also consistent with the rest of the
> decimal module. Thanks for clearing it up.
>
> My concrete use case is printing an arbitrary fraction as a
> user-readable decimal, rounded to the specified number of digits, and
> using the exponential notation where appropriate:
>
> import decimal
> _dec_fmt_context = decimal.Context(prec=15, rounding=decimal.ROUND_HALF_UP)
> def _format(frac):
>     with decimal.localcontext(_dec_fmt_context):
>         dec = decimal.Decimal(frac.numerator) /
> decimal.Decimal(frac.denominator)
>         return '{:g}'.format(dec)
>
> The decimal obtained by dividing the numerator with the denominator
> includes trailing zeros. Calling normalize() to get rid of them will
> have the unfortunate side effect of turning 9806650 into 9.80665e+6,
> and the method recommended in the documentation:
>
> def remove_exponent(d):
>     return d.quantize(decimal.Decimal(1)) if d == d.to_integral() else
> d.normalize()
>
> ...will raise "decimal.InvalidOperation: quantize result has too many
> digits for current context" when the number is too large.

In that case I think you just want to normalize instead. So:

>>> def remove_exponent(d):
...   if d == d.to_integral():
...     try:
...       return d.quantize(1)
...     except decimal.InvalidOperation:
...       pass
...   return d.normalize()
...
>>> remove_exponent(D('123000000'))
Decimal('123000000')
>>> remove_exponent(D('123.4567'))
Decimal('123.4567')
>>> remove_exponent(D('123.4567890'))
Decimal('123.456789')
>>> remove_exponent(D('123e6'))
Decimal('123000000')
>>> remove_exponent(D('1234567890123456789012345678901234567890'))
Decimal('1.234567890123456789012345679E+39')


Reply | Threaded
Open this post in threaded view
|

Floating point "g" format not stripping trailing zeros

Ian Kelly-2
In reply to this post by Grant Edwards-7
On Fri, Feb 13, 2015 at 7:02 AM, Grant Edwards <invalid at invalid.invalid> wrote:

> On 2015-02-12, Ian Kelly <ian.g.kelly at gmail.com> wrote:
>> On Thu, Feb 12, 2015 at 1:23 PM, Hrvoje Nik?i? <hniksic at gmail.com> wrote:
>>
>>> {:.15g} is supposed to give 15 digits of precision, but with trailing
>>> zeros removed.
>>
>> The doc says with "insignificant" trailing zeros removed, not all
>> trailing zeros.
>
> Can somebody explain the difference between "significant" and
> "insignificant" tailing zeros to somebody who barely passed his single
> numerical methods class?  [Though I have, on occasion, had to tinker
> with the innards of SW floating point libraries and could fish a
> hardcopy of IEEE-754 out of a filing cabinet if needed.]

Significant digits are within the precision of the calculation.
Writing 1.230 indicates that the fourth digit is known to be zero.
Writing 1.23 outside a context of exact calculation indicates that the
fourth digit is unknown due to insufficient precision.


Reply | Threaded
Open this post in threaded view
|

Floating point "g" format not stripping trailing zeros

Grant Edwards-7
In reply to this post by Grant Edwards-7
On 2015-02-13, Ian Kelly <ian.g.kelly at gmail.com> wrote:

> On Fri, Feb 13, 2015 at 7:02 AM, Grant Edwards <invalid at invalid.invalid> wrote:
>> On 2015-02-12, Ian Kelly <ian.g.kelly at gmail.com> wrote:
>>> On Thu, Feb 12, 2015 at 1:23 PM, Hrvoje Nik?i? <hniksic at gmail.com> wrote:
>>>
>>>> {:.15g} is supposed to give 15 digits of precision, but with trailing
>>>> zeros removed.
>>>
>>> The doc says with "insignificant" trailing zeros removed, not all
>>> trailing zeros.
>>
>> Can somebody explain the difference between "significant" and
>> "insignificant" tailing zeros to somebody who barely passed his single
>> numerical methods class?  [Though I have, on occasion, had to tinker
>> with the innards of SW floating point libraries and could fish a
>> hardcopy of IEEE-754 out of a filing cabinet if needed.]
>
> Significant digits are within the precision of the calculation.
> Writing 1.230 indicates that the fourth digit is known to be zero.
> Writing 1.23 outside a context of exact calculation indicates that the
> fourth digit is unknown due to insufficient precision.

I knew that, but I was asking in the context of float/decimal's
formatting function.  I didn't realize that float and/or decimal had a
"significant digit" property, and therefore possess significant vs.
insignificant trailing zeros when represented in base-10.

--
Grant Edwards               grant.b.edwards        Yow! Mr and Mrs PED, can I
                                  at               borrow 26.7% of the RAYON
                              gmail.com            TEXTILE production of the
                                                   INDONESIAN archipelago?


Reply | Threaded
Open this post in threaded view
|

Floating point "g" format not stripping trailing zeros

Ian Kelly-2
On Fri, Feb 13, 2015 at 1:33 PM, Grant Edwards <invalid at invalid.invalid> wrote:

> On 2015-02-13, Ian Kelly <ian.g.kelly at gmail.com> wrote:
>> On Fri, Feb 13, 2015 at 7:02 AM, Grant Edwards <invalid at invalid.invalid> wrote:
>>> On 2015-02-12, Ian Kelly <ian.g.kelly at gmail.com> wrote:
>>>> On Thu, Feb 12, 2015 at 1:23 PM, Hrvoje Nik?i? <hniksic at gmail.com> wrote:
>>>>
>>>>> {:.15g} is supposed to give 15 digits of precision, but with trailing
>>>>> zeros removed.
>>>>
>>>> The doc says with "insignificant" trailing zeros removed, not all
>>>> trailing zeros.
>>>
>>> Can somebody explain the difference between "significant" and
>>> "insignificant" tailing zeros to somebody who barely passed his single
>>> numerical methods class?  [Though I have, on occasion, had to tinker
>>> with the innards of SW floating point libraries and could fish a
>>> hardcopy of IEEE-754 out of a filing cabinet if needed.]
>>
>> Significant digits are within the precision of the calculation.
>> Writing 1.230 indicates that the fourth digit is known to be zero.
>> Writing 1.23 outside a context of exact calculation indicates that the
>> fourth digit is unknown due to insufficient precision.
>
> I knew that, but I was asking in the context of float/decimal's
> formatting function.  I didn't realize that float and/or decimal had a
> "significant digit" property, and therefore possess significant vs.
> insignificant trailing zeros when represented in base-10.

A Decimal object consists of a sign, a tuple of decimal digits, and an
exponent. If the digits are, e.g, (1, 2, 3, 0, 0, 0), then that would
be equal to the Decimal with the digits (1, 2, 3) and the same sign
and exponent, but the explicit presence of the trailing zeros
indicates their significance. If this doesn't answer your question,
then I'm not really sure what you're asking.


Reply | Threaded
Open this post in threaded view
|

Floating point "g" format not stripping trailing zeros

Dave Angel-4
In reply to this post by Grant Edwards-7
On 02/13/2015 03:33 PM, Grant Edwards wrote:
> On 2015-02-13, Ian Kelly <ian.g.kelly at gmail.com> wrote:

>> Significant digits are within the precision of the calculation.
>> Writing 1.230 indicates that the fourth digit is known to be zero.
>> Writing 1.23 outside a context of exact calculation indicates that the
>> fourth digit is unknown due to insufficient precision.
>
> I knew that, but I was asking in the context of float/decimal's
> formatting function.  I didn't realize that float and/or decimal had a
> "significant digit" property, and therefore possess significant vs.
> insignificant trailing zeros when represented in base-10.
>

Just jumping in here, and somebody please correct me if I mess it up.

Built-in binary floating point (float) has a fixed number of bits for
mantissa, and separate bits for exponent and sign.  Because of those
fixed number of bits, no assumption can be made as to how many of them
are relevant.

On the other hand, the Decimal package has a way that the programmer can
tell how many digits to use at each stage of the calculation.  So if the
programmer bothered to set it to the correct precision, the print logic
(could) use that to decide about trailing zeroes.  I have no idea
whether it does, but this thread would seem to say it does.

I also have no definite opinion as to whether that's reasonable, or
whether most calculations are done by setting digits to about twice
what's needed, and rounding at the end.  I know I did exactly that when
I wrote a variable length math package to double-check the accuracy of a
fixed precision package I was developing, 40 years ago  (long before the
IEEE-754 standard began meetings).  The fixed precision package was
fast, and used lots of clever(?) shortcuts for speed.  The variable one
was written very brute force, ran maybe 100 times slower (on another
machine), but the results could be compared with automatic algorithms.
For simple arithmetic, not too big a deal, but for transcendentals, the
error analysis was very important.  For example, the fast algorithm was
a custom chebyshev, while the reference implementation was Taylor series.

--
DaveA


Reply | Threaded
Open this post in threaded view
|

Floating point "g" format not stripping trailing zeros

Grant Edwards-7
In reply to this post by Grant Edwards-7
On 2015-02-13, Dave Angel <davea at davea.name> wrote:

> On 02/13/2015 03:33 PM, Grant Edwards wrote:
>> On 2015-02-13, Ian Kelly <ian.g.kelly at gmail.com> wrote:
>>
>>> Significant digits are within the precision of the calculation.
>>> Writing 1.230 indicates that the fourth digit is known to be zero.
>>> Writing 1.23 outside a context of exact calculation indicates that the
>>> fourth digit is unknown due to insufficient precision.
>>
>> I knew that, but I was asking in the context of float/decimal's
>> formatting function.  I didn't realize that float and/or decimal had a
>> "significant digit" property, and therefore possess significant vs.
>> insignificant trailing zeros when represented in base-10.
>
> Just jumping in here, and somebody please correct me if I mess it up.
>
> Built-in binary floating point (float) has a fixed number of bits for
> mantissa, and separate bits for exponent and sign.  Because of those
> fixed number of bits, no assumption can be made as to how many of them
> are relevant.

Right.

> On the other hand, the Decimal package has a way that the programmer
> can tell how many digits to use at each stage of the calculation.

That's what surpised me.  From TFM:

https://docs.python.org/2/library/decimal.html:

 * The decimal module incorporates a notion of significant places so that
   1.30 + 1.20 is 2.50. The trailing zero is kept to indicate
   significance. This is the customary presentation for monetary
   applications. For multiplication, the ?schoolbook? approach uses
   all the figures in the multiplicands. For instance, 1.3 * 1.2 gives
   1.56 while 1.30 * 1.20 gives 1.5600.

> So if the programmer bothered to set it to the correct precision, the
> print logic (could) use that to decide about trailing zeroes.  I have
> no idea whether it does, but this thread would seem to say it does.

It seems to. Pretty cool.

--
Grant Edwards               grant.b.edwards        Yow! LOOK!!  Sullen
                                  at               American teens wearing
                              gmail.com            MADRAS shorts and "Flock of
                                                   Seagulls" HAIRCUTS!


Reply | Threaded
Open this post in threaded view
|

Floating point "g" format not stripping trailing zeros

Ian Kelly-2
On Fri, Feb 13, 2015 at 2:22 PM, Grant Edwards <invalid at invalid.invalid> wrote:

> On 2015-02-13, Dave Angel <davea at davea.name> wrote:
>> On the other hand, the Decimal package has a way that the programmer
>> can tell how many digits to use at each stage of the calculation.
>
> That's what surpised me.  From TFM:
>
> https://docs.python.org/2/library/decimal.html:
>
>  * The decimal module incorporates a notion of significant places so that
>    1.30 + 1.20 is 2.50. The trailing zero is kept to indicate
>    significance. This is the customary presentation for monetary
>    applications. For multiplication, the ?schoolbook? approach uses
>    all the figures in the multiplicands. For instance, 1.3 * 1.2 gives
>    1.56 while 1.30 * 1.20 gives 1.5600.

Huh. That approach for multiplication is definitely not what I was
taught in school. I was taught that the number of significant digits
in the product is the lesser of the number of significant digits in
either of the measured multiplicands. So 1.30 * 1.20 would be 1.56,
while 1.3 * 1.2 would just be 1.6. Wikipedia appears to agree with me:

http://en.wikipedia.org/wiki/Significance_arithmetic#Multiplication_and_division_using_significance_arithmetic

Moreover:

>>> D('1.304') * D('1.204')
Decimal('1.570016')
>>> D('1.295') * D('1.195')
Decimal('1.547525')

So 1.30 * 1.20 could be written approximately as 1.56 ? 0.01. Given
that, I don't understand how the trailing zeros in 1.5600 could
possibly be considered significant.


Reply | Threaded
Open this post in threaded view
|

Floating point "g" format not stripping trailing zeros

Ian Kelly-2
On Fri, Feb 13, 2015 at 2:40 PM, Ian Kelly <ian.g.kelly at gmail.com> wrote:

> On Fri, Feb 13, 2015 at 2:22 PM, Grant Edwards <invalid at invalid.invalid> wrote:
>> On 2015-02-13, Dave Angel <davea at davea.name> wrote:
>>> On the other hand, the Decimal package has a way that the programmer
>>> can tell how many digits to use at each stage of the calculation.
>>
>> That's what surpised me.  From TFM:
>>
>> https://docs.python.org/2/library/decimal.html:
>>
>>  * The decimal module incorporates a notion of significant places so that
>>    1.30 + 1.20 is 2.50. The trailing zero is kept to indicate
>>    significance. This is the customary presentation for monetary
>>    applications. For multiplication, the ?schoolbook? approach uses
>>    all the figures in the multiplicands. For instance, 1.3 * 1.2 gives
>>    1.56 while 1.30 * 1.20 gives 1.5600.
>
> Huh. That approach for multiplication is definitely not what I was
> taught in school. I was taught that the number of significant digits
> in the product is the lesser of the number of significant digits in
> either of the measured multiplicands. So 1.30 * 1.20 would be 1.56,
> while 1.3 * 1.2 would just be 1.6. Wikipedia appears to agree with me:
>
> http://en.wikipedia.org/wiki/Significance_arithmetic#Multiplication_and_division_using_significance_arithmetic
>
> Moreover:
>
>>>> D('1.304') * D('1.204')
> Decimal('1.570016')
>>>> D('1.295') * D('1.195')
> Decimal('1.547525')
>
> So 1.30 * 1.20 could be written approximately as 1.56 ? 0.01. Given
> that, I don't understand how the trailing zeros in 1.5600 could
> possibly be considered significant.

I guess the point here is that the paragraph isn't really talking
about significance arithmetic; it's explaining how it decides how many
digits to keep in the result. It may be fine for 1.30 * 1.20 to return
1.56, but it would be very confusing if 1.35 * 1.25 returned 1.69
instead of 1.6875. The wording of the paragraph seems misleading,
though.


Reply | Threaded
Open this post in threaded view
|

Floating point "g" format not stripping trailing zeros

Mark Lawrence
In reply to this post by Ian Kelly-2
On 12/02/2015 23:46, Ian Kelly wrote:

> On Thu, Feb 12, 2015 at 1:23 PM, Hrvoje Nik?i? <hniksic at gmail.com> wrote:
>>>>>>>> from decimal import Decimal as D
>>>>>>>> x = D(1)/D(999)
>>>>>>>> '{:.15g}'.format(x)
>>>>>
>>>>> '0.00100100100100100'
>> [...]
>>>> I'd say it's a bug.  P is 15, you've got 17 digits after the decimal place
>>>> and two of those are insignificant trailing zeros.
>>>
>>> Actually it's the float version that doesn't match the documentation.
>>> In the decimal version, sure there are 17 digits after the decimal
>>> place there, but the first two -- which are leading zeroes -- would
>>> not normally be considered significant.
>>
>> {:.15g} is supposed to give 15 digits of precision, but with trailing
>> zeros removed.
>
> The doc says with "insignificant" trailing zeros removed, not all
> trailing zeros.
>
>> For example, '{:.15g}'.format(Decimal('0.5')) should
>> yield '0.5', not '0.500000000000000' -- and, it indeed does.  It is
>> only for some numbers that trailing zeros are not removed, which looks
>> like a bug.  The behavior of floats matches both the documentation and
>> other languages using the 'g' decimal format, such as C.
>
> Ah, I see now what's going on here. With floats, there is really no
> notion of significant digits. The values 0.5 and 0.50000 are
> completely equivalent. With decimals, that's not exactly true; if you
> give the decimal a trailing zero then you are telling it that the zero
> is significant.
>
>>>> Decimal('0.5')
> Decimal('0.5')
>>>> Decimal('0.50000')
> Decimal('0.50000')
>>>> Decimal('0.5').as_tuple()
> DecimalTuple(sign=0, digits=(5,), exponent=-1)
>>>> Decimal('0.50000').as_tuple()
> DecimalTuple(sign=0, digits=(5, 0, 0, 0, 0), exponent=-5)
>
> These are distinct; the decimal knows how many significant digits you
> passed it. As a result, these are also distinct:
>
>>>> '{:.4g}'.format(Decimal('0.5'))
> '0.5'
>>>> '{:.4g}'.format(Decimal('0.50000'))
> '0.5000'
>
> Now what happens in your original example of 1/999? The default
> decimal context uses 28 digits of precision, so the result of that
> calculation will have 28 significant digits in it.
>
>>>> decimal.getcontext().prec
> 28
>>>> Decimal(1) / Decimal(999)
> Decimal('0.001001001001001001001001001001')
>
> When you specify the a precision of 15 in your format string, you're
> telling it to take the first 15 of those. It doesn't care that the
> last couple of those are zeros, because as far as it's concerned,
> those digits are significant.
>

I still think it's a bug as the 'p' being referred to in the OP's
original message is "The precision is a decimal number indicating how
many digits should be displayed after the decimal point for a floating
point value formatted with 'f' and 'F', or before and after the decimal
point for a floating point value formatted with 'g' or 'G'".  In other
words is has nothing to do with the precision of the underlying number.


--
My fellow Pythonistas, ask not what our language can do for you, ask
what you can do for our language.

Mark Lawrence



Reply | Threaded
Open this post in threaded view
|

Floating point "g" format not stripping trailing zeros

Ian Kelly-2
On Fri, Feb 13, 2015 at 4:05 PM, Mark Lawrence <breamoreboy at yahoo.co.uk> wrote:
> I still think it's a bug as the 'p' being referred to in the OP's original
> message is "The precision is a decimal number indicating how many digits
> should be displayed after the decimal point for a floating point value
> formatted with 'f' and 'F', or before and after the decimal point for a
> floating point value formatted with 'g' or 'G'".  In other words is has
> nothing to do with the precision of the underlying number.

I read that paragraph as describing generally what the precision
means, with the table below detailing more specifically how it is
used. I think for 'g' this is just trying to contrast it with 'f'
where it really is just the number of digits displayed after the
decimal point. The table entry for 'g' on the other hand quite clearly
says, "for a given precision p >= 1, this rounds the number to p
significant digits".


Reply | Threaded
Open this post in threaded view
|

Floating point "g" format not stripping trailing zeros

Mark Lawrence
On 14/02/2015 00:11, Ian Kelly wrote:

> On Fri, Feb 13, 2015 at 4:05 PM, Mark Lawrence <breamoreboy at yahoo.co.uk> wrote:
>> I still think it's a bug as the 'p' being referred to in the OP's original
>> message is "The precision is a decimal number indicating how many digits
>> should be displayed after the decimal point for a floating point value
>> formatted with 'f' and 'F', or before and after the decimal point for a
>> floating point value formatted with 'g' or 'G'".  In other words is has
>> nothing to do with the precision of the underlying number.
>
> I read that paragraph as describing generally what the precision
> means, with the table below detailing more specifically how it is
> used. I think for 'g' this is just trying to contrast it with 'f'
> where it really is just the number of digits displayed after the
> decimal point. The table entry for 'g' on the other hand quite clearly
> says, "for a given precision p >= 1, this rounds the number to p
> significant digits".
>

With insignificant trailing zeros removed.  So if I'm asking for 15
significant digits *in the output* I don't expect to see those zeros as
I don't see them as significant.  If I did I'd have p set higher.

--
My fellow Pythonistas, ask not what our language can do for you, ask
what you can do for our language.

Mark Lawrence



Reply | Threaded
Open this post in threaded view
|

Floating point "g" format not stripping trailing zeros

Gregory Ewing
In reply to this post by Ian Kelly-2
Mark Lawrence wrote:
> I still think it's a bug as the 'p' being referred to in the OP's
> original message is "The precision is a decimal number indicating how
> many digits should be displayed after the decimal point for a floating
> point value formatted with 'f' and 'F', or before and after the decimal
> point for a floating point value formatted with 'g' or 'G'".  In other
> words is has nothing to do with the precision of the underlying number.

But it does say that "insignificant trailing
zeros are removed", not just "trailing zeros
are removed".

However, it's possible that "insignificant"
was just intended to mean "after the decimal
point". The specification is a bit vague.

--
Greg


12