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

Daniel Fetchinson fetchinson at googlemail.com
Thu Feb 16 09:36:20 CET 2012


>> 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. :-)

Thanks a lot, this indeed mostly works, except for cases when the
error needs to be rounded up and becomes two digits:

>>> format_error( '1.34883', '0.0098' )
'1.349(10)'

But in this case I'd like to see 1.35(1)

Cheers,
Daniel


-- 
Psss, psss, put it down! - http://www.cafepress.com/putitdown



More information about the Python-list mailing list