Significant figures calculation

Discussion in 'Python' started by Harold, Jun 24, 2011.

  1. Harold

    Harold Guest

    Hi,

    I am looking for an easy way to do significant figure calculations in
    python (which I want to use with a class that does unit calculations).
    Significant figure calculations should have the semantics explained,
    e.g., here: http://chemistry.about.com/od/mathsciencefundamentals/a/sigfigures.htm

    My hope was that the decimal module would provide this functionality,
    but:

    >>> print Decimal('32.01') + Decimal(5.325) + Decimal('12')

    49.335 # instead of 49
    >>> print Decimal('25.624') / Decimal('25')

    1.02496 # instead of 1.0
    >>> print Decimal('1.2') == Decimal('1.23')

    False # actually not sure how the semantics should be

    I tried to modify the DecimalContext (e.g. getcontext().prec = 2) but
    that did not lead to the correct behavior. Google and this list didn't
    yield a good answer yet... so I'd be happy to get a good
    recommendations or pointers.

    P.S. I am aware that significant figure calculation is controversial
    and makes implicit assumptions on the probability distributions of the
    variables. I am simply looking for an implementation of the (well
    defined) arithmetics as defined on the cited website.
     
    Harold, Jun 24, 2011
    #1
    1. Advertising

  2. On Fri, 24 Jun 2011 13:05:41 -0700, Harold wrote:

    > Hi,
    >
    > I am looking for an easy way to do significant figure calculations in
    > python (which I want to use with a class that does unit calculations).
    > Significant figure calculations should have the semantics explained,
    > e.g., here:
    > http://chemistry.about.com/od/mathsciencefundamentals/a/sigfigures.htm
    >
    > My hope was that the decimal module would provide this functionality,
    > but:
    >
    >>>> print Decimal('32.01') + Decimal(5.325) + Decimal('12')

    > 49.335 # instead of 49
    >>>> print Decimal('25.624') / Decimal('25')

    > 1.02496 # instead of 1.0
    >>>> print Decimal('1.2') == Decimal('1.23')

    > False # actually not sure how the semantics should be
    >
    > I tried to modify the DecimalContext (e.g. getcontext().prec = 2) but
    > that did not lead to the correct behavior.


    Really? It works for me.


    >>> import decimal
    >>> D = decimal.Decimal
    >>> decimal.getcontext().prec = 2
    >>>
    >>> D('32.01') + D('5.325') + D('12')

    Decimal('49')
    >>>
    >>> D('25.624') / D('25')

    Decimal('1.0')

    The only thing you have to watch out for is this:

    >>> D('1.2') == D('1.23') # no rounding

    False
    >>> D('1.2') == +D('1.23') # force rounding

    True



    --
    Steven
     
    Steven D'Aprano, Jun 24, 2011
    #2
    1. Advertising

  3. Harold

    Jerry Hill Guest

    On Fri, Jun 24, 2011 at 4:46 PM, Steven D'Aprano
    <> wrote:
    > Really? It works for me.
    >
    >>>> import decimal
    >>>> D = decimal.Decimal
    >>>> decimal.getcontext().prec = 2
    >>>>
    >>>> D('32.01') + D('5.325') + D('12')

    > Decimal('49')


    I'm curious. Is there a way to get the number of significant digits
    for a particular Decimal instance? I spent a few minutes browsing
    through the docs, and didn't see anything obvious. I was thinking
    about setting the precision dynamically within a function, based on
    the significance of the inputs.

    --
    Jerry
     
    Jerry Hill, Jun 24, 2011
    #3
  4. Harold

    Guest

    Jerry Hill wrote:

    > I'm curious. Is there a way to get the number of significant digits
    > for a particular Decimal instance? I spent a few minutes browsing
    > through the docs, and didn't see anything obvious. I was thinking
    > about setting the precision dynamically within a function, based on
    > the significance of the inputs.


    Not officially, so far as I know, but if you're willing to risk using a
    private implementation detail that is subject to change:

    >>> decimal.Decimal('000123.45000')._int

    '12345000'

    However, sometimes you may need to inspect the exponent as well:

    >>> zero = decimal.Decimal('0.00000000')
    >>> zero._int

    '0'
    >>> zero._exp

    -8



    --
    Steven
     
    , Jun 25, 2011
    #4
  5. Harold

    Harold Guest

    > > I tried to modify the DecimalContext (e.g. getcontext().prec = 2) but
    > > that did not lead to the correct behavior.

    >
    > Really? It works for me.


    You are right, I did something wrong when attempting to set the
    precision.
    And the trick with rounding the decimal with the unary + is neat.
    It's the first time for me to play with decimals, so bare with me if I
    miss
    the obvious.

    However, this approach forces you to know the number of significant
    figures
    beforehand -- which is precisely what the arithmetics should do for
    you.
    What would indeed be nice, is Jerry's suggestion to obtain the number
    of
    significant bits and write a small wrapper around the number protocoll
    implementation that accounts significance and have __str__/__repr__
    set
    the context dynamically.

    I haven't seen anything obvious in the docs, though it might be
    possible
    to use log10 of the length of some normalized string representation.
     
    Harold, Jun 25, 2011
    #5
  6. Harold

    Chris Torek Guest

    In article <>
    Jerry Hill <> wrote:
    >I'm curious. Is there a way to get the number of significant digits
    >for a particular Decimal instance?


    Yes:

    def sigdig(x):
    "return the number of significant digits in x"
    return len(x.as_tuple()[1])

    import decimal
    D = decimal.Decimal

    for x in (
    '1',
    '1.00',
    '1.23400e-8',
    '0.003'
    ):
    print 'sigdig(%s): %d' % (x, sigdig(D(x)))
    --
    In-Real-Life: Chris Torek, Wind River Systems
    Intel require I note that my opinions are not those of WRS or Intel
    Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
    email: gmail (figure it out) http://web.torek.net/torek/index.html
     
    Chris Torek, Jun 25, 2011
    #6
  7. Harold

    Harold Guest

    > >I'm curious.  Is there a way to get the number of significant digits
    > >for a particular Decimal instance?

    >
    > Yes:
    >
    > def sigdig(x):
    >     "return the number of significant digits in x"
    >     return len(x.as_tuple()[1])


    Great! that's exactly what I needed.
    thanks Chris!
     
    Harold, Jun 26, 2011
    #7
  8. Harold

    Dave Angel Guest

    (You top-posted your reply, instead of writing your response following
    the part you were quoting)

    On 01/-10/-28163 02:59 PM, Lalitha Prasad K wrote:
    > In numerical analysis there is this concept of machine zero, which is
    > computed like this:
    >
    > e=1.0
    > while 1.0+e> 1.0:
    > e=e/2.0
    > print e
    >
    > The number e will give you the precision of floating point numbers.
    >
    > Lalitha Prasad
    >


    That particular algorithm is designed for binary floating point. The OP
    was asking about Decimal instances. So you'd want to divide by 10.0
    each time. And of course you'd want to do it with Decimal objects.
    > On Sun, Jun 26, 2011 at 9:05 PM, Harold<> wrote:
    >
    >>>> I'm curious. Is there a way to get the number of significant digits
    >>>> for a particular Decimal instance?


    DaveA
     
    Dave Angel, Jun 27, 2011
    #8
  9. Harold

    Harold Guest

    On Jun 25, 9:04 pm, Chris Torek <> wrote:
    > >I'm curious.  Is there a way to get the number of significant digits
    > >for a particular Decimal instance?

    >
    > Yes:
    >
    > def sigdig(x):
    >     "return the number of significant digits in x"
    >     return len(x.as_tuple()[1])


    Great, Chris, this is (almost) exactly what I needed.
    To make it work for numbers like 1200, that have four digits but only
    two of them being significant, I changed your snippet to the
    following:

    class Empirical(Decimal) :
    @property
    def significance(self) :
    t = self.as_tuple()
    if t[2] < 0 :
    return len(t[1])
    else :
    return len(''.join(map(str,t[1])).rstrip('0'))


    >>> Empirical('1200.').significance

    2
    >>> Empirical('1200.0').significance

    5

    now it's only about overriding the numerical operators :)
     
    Harold, Jun 27, 2011
    #9
  10. Harold

    Ethan Furman Guest

    Harold wrote:
    > On Jun 25, 9:04 pm, Chris Torek <> wrote:
    >>> I'm curious. Is there a way to get the number of significant digits
    >>> for a particular Decimal instance?

    >> Yes:
    >>
    >> def sigdig(x):
    >> "return the number of significant digits in x"
    >> return len(x.as_tuple()[1])

    >
    > Great, Chris, this is (almost) exactly what I needed.
    > To make it work for numbers like 1200, that have four digits but only
    > two of them being significant, I changed your snippet to the
    > following:
    >
    > class Empirical(Decimal) :
    > @property
    > def significance(self) :
    > t = self.as_tuple()
    > if t[2] < 0 :
    > return len(t[1])
    > else :
    > return len(''.join(map(str,t[1])).rstrip('0'))
    >
    >
    >>>> Empirical('1200.').significance

    > 2
    >>>> Empirical('1200.0').significance

    > 5


    What about when 1200 is actually 4 significant digits? Or 3?

    ~Ethan~
     
    Ethan Furman, Jun 27, 2011
    #10
  11. Harold

    Ethan Furman Guest

    Harold Fellermann wrote:
    > Hi Ethan,
    >
    >>>>>> Empirical('1200.').significance
    >>> 2
    >>>>>> Empirical('1200.0').significance
    >>> 5

    >> What about when 1200 is actually 4 significant digits? Or 3?

    >
    > Then you'd simply write 1.200e3 and 1.20e3, respectively.
    > That's just how the rules are defined.


    But your code is not following them:

    Python 3.2 (r32:88445, Feb 20 2011, 21:29:02) [MSC v.1500 32 bit
    (Intel)] on win32
    Type "help", "copyright", "credits" or "license" for more information.
    --> from decimal import Decimal
    --> class Empirical(Decimal) :
    .... @property
    .... def significance(self) :
    .... t = self.as_tuple()
    .... if t[2] < 0 :
    .... return len(t[1])
    .... else :
    .... return len(''.join(map(str,t[1])).rstrip('0'))
    ....
    --> Empirical('1.200E+3').significance
    2 # should be four
    --> Empirical('1.20E+3').significance
    2 # should be three
    --> Empirical('1.20E+4').significance
    2 # should be three

    The negatives appear to work, though:
    --> Empirical('1.20E-4').significance
    3
    --> Empirical('1.2819E-3').significance
    5
    --> Empirical('1.2819E-1').significance
    5
    --> Empirical('1.281900E-1').significance
    7

    ~Ethan~
     
    Ethan Furman, Jun 27, 2011
    #11
  12. On Tue, 28 Jun 2011 06:53 am Ethan Furman wrote:

    > Harold wrote:

    [...]
    >>>>> Empirical('1200.').significance

    >> 2


    Well, that's completely wrong. It should be 4.

    >>>>> Empirical('1200.0').significance

    >> 5

    >
    > What about when 1200 is actually 4 significant digits? Or 3?


    Then you shouldn't write it as 1200.0. By definition, zeros on the right are
    significant. If you don't want zeroes on the right to count, you have to
    not show them.

    Five sig figures: 1200.0
    Four sig figures: 1200
    Three sig figures: 1.20e3
    Two sig figures: 1.2e3
    One sig figure: 1e3
    Zero sig figure: 0



    --
    Steven
     
    Steven D'Aprano, Jun 28, 2011
    #12
  13. On Tue, Jun 28, 2011 at 12:56 PM, Steven D'Aprano
    <> wrote:
    > Zero sig figure: 0
    >


    Is 0.0 one sig fig or two? (Just vaguely curious. Also curious as to
    whether a zero sig figures value is ever useful.)

    ChrisA
     
    Chris Angelico, Jun 28, 2011
    #13
  14. On Tue, 28 Jun 2011 01:16 pm Chris Angelico wrote:

    > On Tue, Jun 28, 2011 at 12:56 PM, Steven D'Aprano
    > <> wrote:
    >> Zero sig figure: 0
    >>

    >
    > Is 0.0 one sig fig or two? (Just vaguely curious. Also curious as to
    > whether a zero sig figures value is ever useful.)


    Two. I was actually being slightly silly about zero fig figures.

    Although, I suppose, if you genuinely had zero significant figures, you
    couldn't tell what the number was at all, so you'd need to use a NaN :)


    --
    Steven
     
    Steven D'Aprano, Jun 28, 2011
    #14
  15. Harold

    Mel Guest

    Erik Max Francis wrote:

    > Chris Angelico wrote:
    >> On Tue, Jun 28, 2011 at 12:56 PM, Steven D'Aprano
    >> <> wrote:
    >>> Zero sig figure: 0

    >
    > That's not really zero significant figures; without further
    > qualification, it's one.
    >
    >> Is 0.0 one sig fig or two?

    >
    > Two.
    >
    >> (Just vaguely curious. Also curious as to
    >> whether a zero sig figures value is ever useful.)

    >
    > Yes. They're order of magnitude estimates. 1 x 10^6 has one
    > significant figure. 10^6 has zero.


    By convention, nobody ever talks about 1 x 9.97^6 .

    Mel.
     
    Mel, Jun 28, 2011
    #15
  16. Chris Angelico, Jun 28, 2011
    #16
  17. Harold

    Mel Guest

    Erik Max Francis wrote:

    > Mel wrote:
    >> Erik Max Francis wrote:
    >>
    >>> Chris Angelico wrote:
    >>>> On Tue, Jun 28, 2011 at 12:56 PM, Steven D'Aprano
    >>>> <> wrote:
    >>>>> Zero sig figure: 0
    >>> That's not really zero significant figures; without further
    >>> qualification, it's one.
    >>>
    >>>> Is 0.0 one sig fig or two?
    >>> Two.
    >>>
    >>>> (Just vaguely curious. Also curious as to
    >>>> whether a zero sig figures value is ever useful.)
    >>> Yes. They're order of magnitude estimates. 1 x 10^6 has one
    >>> significant figure. 10^6 has zero.

    >>
    >> By convention, nobody ever talks about 1 x 9.97^6 .

    >
    > Not sure what the relevance is, since nobody had mentioned any such thing.
    >
    > If it was intended as a gag, I don't catch the reference.


    I get giddy once in a while.. push things to limits. It doesn't really mean
    anything. The point was that it's only the 2 in a number like 2e6 that is
    taken to have error bars. The 6 is always an absolute number. As is the 10
    in 2*10**6. The thought also crossed my mind of a kind of continued
    fraction in reverse -- 2e1.3e.7 . I managed to keep quiet about that one.

    Mel.
     
    Mel, Jun 28, 2011
    #17
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Steve
    Replies:
    5
    Views:
    42,726
    Steve
    May 17, 2004
  2. Jeremy Watts

    more than 16 significant figures

    Jeremy Watts, Jul 4, 2005, in forum: Java
    Replies:
    32
    Views:
    1,341
    Tom N
    Jul 15, 2005
  3. Alf P. Steinbach

    Re: Round to significant figures (C++)

    Alf P. Steinbach, May 1, 2006, in forum: C++
    Replies:
    0
    Views:
    739
    Alf P. Steinbach
    May 1, 2006
  4. Max Williams
    Replies:
    6
    Views:
    126
    Rob Biedenharn
    May 16, 2009
  5. SMH
    Replies:
    0
    Views:
    235
Loading...

Share This Page