diff --git a/django/utils/numberformat.py b/django/utils/numberformat.py
index 6a31237..7939d8d 100644
--- a/django/utils/numberformat.py
+++ b/django/utils/numberformat.py
@@ -3,8 +3,7 @@ from django.utils.safestring import mark_safe
 from django.utils import six
 
 
-def format(number, decimal_sep, decimal_pos=None, grouping=0, thousand_sep='',
-           force_grouping=False):
+def format(number, decimal_sep, decimal_pos=None, grouping=0, thousand_sep='', force_grouping=False):
     """
     Gets a number (as a number or string), and returns it as a string,
     using formats defined as arguments:
@@ -17,32 +16,42 @@ def format(number, decimal_sep, decimal_pos=None, grouping=0, thousand_sep='',
     use_grouping = settings.USE_L10N and settings.USE_THOUSAND_SEPARATOR
     use_grouping = use_grouping or force_grouping
     use_grouping = use_grouping and grouping > 0
+    use_grouping = use_grouping and thousand_sep
+
     # Make the common case fast
     if isinstance(number, int) and not use_grouping and not decimal_pos:
         return mark_safe(six.text_type(number))
-    # sign
-    sign = ''
+
+    float_number = float(number)
     str_number = six.text_type(number)
-    if str_number[0] == '-':
+
+    if decimal_pos is not None:
+        # Use the %f format string. This gives us rounding and the
+        # right number of decimal positions automatically. This also
+        # removes any 'e' if the number is really small or really large
+        str_number = six.text_type(('%.' + str(decimal_pos) + 'f') % float_number)
+
+    if not use_grouping:
+        return str_number.replace('.', decimal_sep)
+
+    # For grouping, we need to separate the sign, int_part and decimal part
+    if float_number < 0:
         sign = '-'
         str_number = str_number[1:]
-    # decimal part
-    if '.' in str_number:
+    else:
+        sign = ''
+
+    if decimal_pos is not None or '.' in str_number:
         int_part, dec_part = str_number.split('.')
-        if decimal_pos is not None:
-            dec_part = dec_part[:decimal_pos]
+        dec_part = decimal_sep + dec_part
     else:
         int_part, dec_part = str_number, ''
-    if decimal_pos is not None:
-        dec_part = dec_part + ('0' * (decimal_pos - len(dec_part)))
-    if dec_part:
-        dec_part = decimal_sep + dec_part
-    # grouping
-    if use_grouping:
-        int_part_gd = ''
-        for cnt, digit in enumerate(int_part[::-1]):
-            if cnt and not cnt % grouping:
-                int_part_gd += thousand_sep
-            int_part_gd += digit
-        int_part = int_part_gd[::-1]
-    return sign + int_part + dec_part
+
+    first_part_len = len(int_part) % grouping
+    full_parts = int_part[first_part_len:]
+    groups = [full_parts[i:i + grouping] for i in range(0, len(full_parts), grouping)]
+
+    if first_part_len > 0:
+        groups.insert(0, int_part[0:first_part_len])
+
+    return sign + thousand_sep.join(groups) + dec_part
diff --git a/tests/i18n/tests.py b/tests/i18n/tests.py
index 1022c8d..1368dbe 100644
--- a/tests/i18n/tests.py
+++ b/tests/i18n/tests.py
@@ -357,14 +357,22 @@ class FormattingTests(TestCase):
         Localization of numbers
         """
         with self.settings(USE_THOUSAND_SEPARATOR=False):
-            self.assertEqual('66666.66', nformat(self.n, decimal_sep='.', decimal_pos=2, grouping=3, thousand_sep=','))
-            self.assertEqual('66666A6', nformat(self.n, decimal_sep='A', decimal_pos=1, grouping=1, thousand_sep='B'))
-            self.assertEqual('66666', nformat(self.n, decimal_sep='X', decimal_pos=0, grouping=1, thousand_sep='Y'))
+            self.assertEqual('66666.67', nformat(self.n, decimal_sep='.', decimal_pos=2, grouping=3, thousand_sep=','))
+            self.assertEqual('66666A7', nformat(self.n, decimal_sep='A', decimal_pos=1, grouping=1, thousand_sep='B'))
+            self.assertEqual('66667', nformat(self.n, decimal_sep='X', decimal_pos=0, grouping=1, thousand_sep='Y'))
+
+            # ticket #14317
+            self.assertEqual(u'0,00', nformat(0.000000001, decimal_sep=',', decimal_pos=2))
+            self.assertEqual(u'0,00', nformat(0.00000000000099, decimal_sep=',', decimal_pos=2))
+            self.assertEqual(u'0,00', nformat(decimal.Decimal(1e-37), decimal_sep=',', decimal_pos=2))
+            self.assertEqual(u'0,67', nformat(0.666, decimal_sep=',', decimal_pos=2))
+            self.assertEqual(u'0,67', nformat('0.666', decimal_sep=',', decimal_pos=2))
 
         with self.settings(USE_THOUSAND_SEPARATOR=True):
-            self.assertEqual('66,666.66', nformat(self.n, decimal_sep='.', decimal_pos=2, grouping=3, thousand_sep=','))
-            self.assertEqual('6B6B6B6B6A6', nformat(self.n, decimal_sep='A', decimal_pos=1, grouping=1, thousand_sep='B'))
-            self.assertEqual('-66666.6', nformat(-66666.666, decimal_sep='.', decimal_pos=1))
+            self.assertEqual('66,666.67', nformat(self.n, decimal_sep='.', decimal_pos=2, grouping=3, thousand_sep=','))
+            self.assertEqual('6B6B6B6B6A7', nformat(self.n, decimal_sep='A', decimal_pos=1, grouping=1, thousand_sep='B'))
+            self.assertEqual('-66666.7', nformat(-66666.666, decimal_sep='.', decimal_pos=1))
+
             self.assertEqual('-66666.0', nformat(int('-66666'), decimal_sep='.', decimal_pos=1))
             self.assertEqual('10000.0', nformat(self.l, decimal_sep='.', decimal_pos=1))
             # This unusual grouping/force_grouping combination may be triggered by the intcomma filter (#17414)
