diff options
Diffstat (limited to 'Lib/decimal.py')
-rw-r--r-- | Lib/decimal.py | 50 |
1 files changed, 50 insertions, 0 deletions
diff --git a/Lib/decimal.py b/Lib/decimal.py index 1f6218cd4a..197269c7d7 100644 --- a/Lib/decimal.py +++ b/Lib/decimal.py @@ -136,6 +136,7 @@ __all__ = [ import numbers as _numbers import copy as _copy +import math as _math try: from collections import namedtuple as _namedtuple @@ -654,6 +655,38 @@ class Decimal(_numbers.Real): raise TypeError("Cannot convert %r to Decimal" % value) + @classmethod + def from_float(cls, f): + """Converts a float to a decimal number, exactly. + + Note that Decimal.from_float(0.1) is not the same as Decimal('0.1'). + Since 0.1 is not exactly representable in binary floating point, the + value is stored as the nearest representable value which is + 0x1.999999999999ap-4. The exact equivalent of the value in decimal + is 0.1000000000000000055511151231257827021181583404541015625. + + >>> Decimal.from_float(0.1) + Decimal('0.1000000000000000055511151231257827021181583404541015625') + >>> Decimal.from_float(float('nan')) + Decimal('NaN') + >>> Decimal.from_float(float('inf')) + Decimal('Infinity') + >>> Decimal.from_float(-float('inf')) + Decimal('-Infinity') + >>> Decimal.from_float(-0.0) + Decimal('-0') + + """ + if isinstance(f, int): # handle integer inputs + return cls(f) + if _math.isinf(f) or _math.isnan(f): # raises TypeError if not a float + return cls(repr(f)) + sign = 0 if _math.copysign(1.0, f) == 1.0 else 1 + n, d = abs(f).as_integer_ratio() + k = d.bit_length() - 1 + result = _dec_from_triple(sign, str(n*5**k), -k) + return result if cls is Decimal else cls(result) + def _isnan(self): """Returns whether the number is not actually one. @@ -3830,6 +3863,23 @@ class Context(object): "diagnostic info too long in NaN") return d._fix(self) + def create_decimal_from_float(self, f): + """Creates a new Decimal instance from a float but rounding using self + as the context. + + >>> context = Context(prec=5, rounding=ROUND_DOWN) + >>> context.create_decimal_from_float(3.1415926535897932) + Decimal('3.1415') + >>> context = Context(prec=5, traps=[Inexact]) + >>> context.create_decimal_from_float(3.1415926535897932) + Traceback (most recent call last): + ... + decimal.Inexact: None + + """ + d = Decimal.from_float(f) # An exact conversion + return d._fix(self) # Apply the context rounding + # Methods def abs(self, a): """Returns the absolute value of the operand. |