Repository: mpmath/mpmath (python)

Issue:
Support for '%' presentation type in `mpf.__format__`
I've been working on these presentation types, to complete the set.
'%' is straightforward, just multiply the `mpf` by 100 and add a % sign at the end.

'n' is a lot trickier, since some locales may be difficult to program.
For instance, 
```python
>>> import locale
>>> locale.setlocale(locale.LC_ALL, 'en_IN.utf8')
>>> print('{:.10n}'.format(100000.25))
1,00,000.25
```
Would a partial implementation of 'n' (i.e., only care about thousands and decimal separators) be useful or should we not bother at all with it? 



Pull request:
Support '%' presentation type for mpf
Closes #837

Patch:
diff --git a/mpmath/ctx_mp_python.py b/mpmath/ctx_mp_python.py
index 11cc597..4f452f2 100644
--- a/mpmath/ctx_mp_python.py
+++ b/mpmath/ctx_mp_python.py
@@ -356,7 +356,8 @@ def __rpow__(s, t):
         return t ** s
 
     def __format__(s, format_spec):
-        return format_mpf(s._mpf_, format_spec)
+        _, _, (prec, _) = s._ctxdata
+        return format_mpf(s._mpf_, format_spec, prec)
 
     def sqrt(s):
         return s.context.sqrt(s)
diff --git a/mpmath/libmp/libmpf.py b/mpmath/libmp/libmpf.py
index f5280d6..8f5afc4 100644
--- a/mpmath/libmp/libmpf.py
+++ b/mpmath/libmp/libmpf.py
@@ -1397,7 +1397,7 @@ def from_str(x, prec=0, rnd=round_fast, base=0):
     (?P<thousands_separators>[,_])?
     (?:\.(?P<precision>0|[1-9][0-9]*))?
     (?P<rounding>[UDYZN])?
-    (?P<type>[eEfFgG])?
+    (?P<type>[eEfFgG%])?
 """, re.DOTALL | re.VERBOSE).fullmatch
 
 _GMPY_ROUND_CHAR_DICT = {
@@ -1488,6 +1488,8 @@ def format_fixed(s,
                  base=10,
                  alternate=False,
                  no_neg_0=False,
+                 percent=False,
+                 prec=0,
                  rounding=round_nearest):
     '''
     Format a number into fixed point.
@@ -1496,6 +1498,9 @@ def format_fixed(s,
     Does not perform padding or aligning
     '''
 
+    if percent:
+        s = mpf_mul(s, from_int(100), prec=prec, rnd=round_nearest)
+
     # First, get the exponent to know how many digits we will need
     _, _, exponent = to_digits_exp(s, 1, base)
 
@@ -1568,6 +1573,9 @@ def format_fixed(s,
     if digits[-1] == "." and strip_last_zero:
         digits = digits[:-1]
 
+    if percent:
+        digits = digits + '%'
+
     return sign, digits
 
 
@@ -1608,7 +1616,7 @@ def format_scientific(s,
     return sign, digits + sep + f'{exponent:+03d}'
 
 
-def format_mpf(num, format_spec):
+def format_mpf(num, format_spec, prec):
     format_dict = read_format_spec(format_spec)
 
     capitalize = False
@@ -1616,6 +1624,11 @@ def format_mpf(num, format_spec):
         capitalize = True
 
     fmt_type = format_dict['type'].lower()
+    percent = False
+    if fmt_type == '%':
+        percent = True
+        fmt_type = 'f'
+
     precision = format_dict['precision']
 
     digits = ''
@@ -1664,6 +1677,8 @@ def format_mpf(num, format_spec):
                 base=10,
                 alternate=format_dict['alternate'],
                 no_neg_0=format_dict['no_neg_0'],
+                percent=percent,
+                prec=prec,
                 rounding=rounding
                 )
     else:  # The format type is scientific


Test patch:
diff --git a/mpmath/tests/test_format.py b/mpmath/tests/test_format.py
index e1d2fb6..6d2e477 100644
--- a/mpmath/tests/test_format.py
+++ b/mpmath/tests/test_format.py
@@ -63,7 +63,7 @@ def random_fmt():
         fmt_str += '.' + str(random.randint(1, 40))
 
     # Type
-    fmt_str += random.choice('fFgGeE')
+    fmt_str += random.choice('fFgGeE%')
     fmt_str += '}'
 
     return fmt_str