Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 39 additions & 34 deletions si_prefix/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
# coding: utf-8
from __future__ import division

import math
import re

from ._version import get_versions

__version__ = get_versions()['version']
del get_versions

Expand All @@ -30,6 +32,7 @@
#: .. _`Forum post`: https://mail.python.org/pipermail/python-list/2009-February/525913.html
#: .. _`The International System of Units (SI) report`: https://www.bipm.org/utils/common/pdf/si_brochure_8_en.pdf
SI_PREFIX_UNITS = u"yzafpnµm kMGTPEZY"

#: .. versionchanged:: 1.0
#: Use unicode string for SI unit to support micro (i.e., µ) character.
#:
Expand All @@ -38,14 +41,13 @@
#: `Issue #4`_.
#:
#: .. _`Issue #4`: https://github.com/cfobel/si-prefix/issues/4
CRE_SI_NUMBER = re.compile(r'\s*(?P<sign>[\+\-])?'
r'(?P<integer>\d+)'
r'(?P<fraction>.\d+)?\s*'
u'(?P<si_unit>[%s])?\s*' % SI_PREFIX_UNITS)
CRE_SI_NUMBER = re.compile(r'^\s*(?P<number>(?P<integer>[+\-]?\d+)?'
r'(?P<fraction>.\d+)?)\s*'
r'(?P<si_unit>[' + SI_PREFIX_UNITS + r'])?\s*$')


def split(value, precision=1):
'''
"""
Split `value` into value and "exponent-of-10", where "exponent-of-10" is a
multiple of 3. This corresponds to SI prefixes.

Expand Down Expand Up @@ -74,7 +76,7 @@ def split(value, precision=1):
si_prefix.split(4781.123) -> (4.8, 3)

See :func:`si_format` for more examples.
'''
"""
negative = False
digits = precision + 1

Expand Down Expand Up @@ -107,7 +109,7 @@ def split(value, precision=1):


def prefix(expof10):
'''
"""
Args:

expof10 : Exponent of a power of 10 associated with a SI unit
Expand All @@ -116,7 +118,7 @@ def prefix(expof10):
Returns:

str : One of the characters in "yzafpnum kMGTPEZY".
'''
"""
prefix_levels = (len(SI_PREFIX_UNITS) - 1) // 2
si_level = expof10 // 3

Expand All @@ -126,8 +128,8 @@ def prefix(expof10):


def si_format(value, precision=1, format_str=u'{value} {prefix}',
exp_format_str=u'{value}e{expof10}'):
'''
exp_format_str=u'{value}e{expof10}', trailing_zeroes=False):
"""
Format value to string with SI prefix, using the specified precision.

Parameters
Expand All @@ -136,15 +138,17 @@ def si_format(value, precision=1, format_str=u'{value} {prefix}',
Input value.
precision : int
Number of digits after decimal place to include.
format_str : str or unicode
exp_format_str : str or unicode
Format string where ``{prefix}`` and ``{value}`` represent the SI
prefix and the value (scaled according to the prefix), respectively.
The default format matches the `SI prefix style`_ format.
exp_str : str or unicode
format_str : str or unicode
Format string where ``{expof10}`` and ``{value}`` represent the
exponent of 10 and the value (scaled according to the exponent of 10),
respectively. This format is used if the absolute exponent of 10 value
is greater than 24.
trailing_zeroes : bool
Include trailing zeroes if number of decimals in value is less than the precision

Returns
-------
Expand Down Expand Up @@ -205,24 +209,27 @@ def si_format(value, precision=1, format_str=u'{value} {prefix}',

.. _`Issue #4`: https://github.com/cfobel/si-prefix/issues/4
.. _SI prefix style:
http://physics.nist.gov/cuu/Units/checklist.html
'''
https://physics.nist.gov/cuu/Units/checklist.html
"""
svalue, expof10 = split(value, precision)
value_format = u'%%.%df' % precision
value_str = value_format % svalue
if trailing_zeroes:
value_str = f"{svalue:.{precision}f}"
else:
# rounds to precision without trailing zeroes which drives me insane
value_str = f"{round(svalue, precision):g}"
try:
return format_str.format(value=value_str,
prefix=prefix(expof10).strip())
prefix=prefix(expof10).strip()).strip()
except ValueError:
sign = ''
if expof10 > 0:
sign = "+"
return exp_format_str.format(value=value_str,
expof10=''.join([sign, str(expof10)]))
expof10=''.join([sign, str(expof10)])).strip()


def si_parse(value):
'''
"""
Parse a value expressed using SI prefix units to a floating point number.

Parameters
Expand All @@ -240,22 +247,20 @@ def si_parse(value):
`Issue #4`_.

.. _`Issue #4`: https://github.com/cfobel/si-prefix/issues/4
'''
CRE_10E_NUMBER = re.compile(r'^\s*(?P<integer>[\+\-]?\d+)?'
"""
CRE_10E_NUMBER = re.compile(r'^\s*(?P<integer>[+\-]?\d+)?'
r'(?P<fraction>.\d+)?\s*([eE]\s*'
r'(?P<expof10>[\+\-]?\d+))?$')
CRE_SI_NUMBER = re.compile(r'^\s*(?P<number>(?P<integer>[\+\-]?\d+)?'
r'(?P<fraction>.\d+)?)\s*'
u'(?P<si_unit>[%s])?\s*$' % SI_PREFIX_UNITS)
r'(?P<expof10>[+\-]?\d+))?$')

match = CRE_10E_NUMBER.match(value)
if match:
# Can be parse using `float`.
assert(match.group('integer') is not None or
match.group('fraction') is not None)
assert (match.group('integer') is not None or
match.group('fraction') is not None)
return float(value)
match = CRE_SI_NUMBER.match(value)
assert(match.group('integer') is not None or
match.group('fraction') is not None)
assert (match.group('integer') is not None or
match.group('fraction') is not None)
d = match.groupdict()
si_unit = d['si_unit'] if d['si_unit'] else ' '
prefix_levels = (len(SI_PREFIX_UNITS) - 1) // 2
Expand All @@ -264,7 +269,7 @@ def si_parse(value):


def si_prefix_scale(si_unit):
'''
"""
Parameters
----------
si_unit : str
Expand All @@ -274,12 +279,12 @@ def si_prefix_scale(si_unit):
-------
int
Multiple associated with `si_unit`, e.g., 1000 for `si_unit=k`.
'''
"""
return 10 ** si_prefix_expof10(si_unit)


def si_prefix_expof10(si_unit):
'''
"""
Parameters
----------
si_unit : str
Expand All @@ -290,6 +295,6 @@ def si_prefix_expof10(si_unit):
int
Exponent of the power of ten associated with `si_unit`, e.g., 3 for
`si_unit=k` and -6 for `si_unit=µ`.
'''
"""
prefix_levels = (len(SI_PREFIX_UNITS) - 1) // 2
return (3 * (SI_PREFIX_UNITS.index(si_unit) - prefix_levels))
return 3 * (SI_PREFIX_UNITS.index(si_unit) - prefix_levels)