Issue 4796: Add from_float methods to the decimal module.
diff --git a/Lib/decimal.py b/Lib/decimal.py
index 1f6218c..197269c 100644
--- a/Lib/decimal.py
+++ b/Lib/decimal.py
@@ -136,6 +136,7 @@
 
 import numbers as _numbers
 import copy as _copy
+import math as _math
 
 try:
     from collections import namedtuple as _namedtuple
@@ -654,6 +655,38 @@
 
         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 @@
                                      "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.