format a measurement result and its error in "scientific" way

Ian Kelly ian.g.kelly at gmail.com
Wed Feb 15 20:56:30 EST 2012


On Wed, Feb 15, 2012 at 5:18 PM, Daniel Fetchinson
<fetchinson at googlemail.com> wrote:
> Hi folks, often times in science one expresses a value (say
> 1.03789291) and its error (say 0.00089) in a short way by parentheses
> like so: 1.0379(9)
>
> One can vary things a bit, but let's take the simplest case when we
> only keep 1 digit of the error (and round it of course) and round the
> value correspondingly. I've been searching around for a simple
> function that would take 2 float arguments and would return a string
> but didn't find anything although something tells me it's been done a
> gazillion times.
>
> What would be the simplest such function?

Well, this basically works:

>>> def format_error(value, error):
...     precision = int(math.floor(math.log(error, 10)))
...     format = "%%.%df(%%d)" % max(-precision, 0)
...     return format % (round(value, -precision),
...                      int(round(error / 10 ** precision)))
...
>>> format_error(1.03789291, 0.00089)
'1.0379(9)'

Note that "math.floor(math.log(error, 10))" may return the wrong
decimal precision due to binary floating point rounding error, which
could produce some strange results:

>>> format_error(10378929, 1000)
'10378900(10)'

So you'll probably want to use decimals instead:

def format_error(value, error):
    value = decimal.Decimal(value)
    error = decimal.Decimal(error)
    value_scale = value.log10().to_integral(decimal.ROUND_FLOOR)
    error_scale = error.log10().to_integral(decimal.ROUND_FLOOR)
    precision = value_scale - error_scale
    if error_scale > 0:
        format = "%%.%dE" % max(precision, 0)
    else:
        format = "%%.%dG" % (max(precision, 0) + 1)
    value_str = format % value.quantize(decimal.Decimal("10") ** error_scale)
    error_str = '(%d)' % error.scaleb(-error_scale).to_integral()
    if 'E' in value_str:
        index = value_str.index('E')
        return value_str[:index] + error_str + value_str[index:]
    else:
        return value_str + error_str

>>> format_error(1.03789291, 0.00089)
'1.0379(9)'
>>> format_error(103789291, 1000)
'1.03789(1)E+08'

I haven't tested this thoroughly, so use at your own risk. :-)

Cheers,
Ian



More information about the Python-list mailing list