Repository: mpmath/mpmath (python)

Issue:
Incompatible (wrt CPython) formatting for format(mpmath.mpf(9.995074823339339e-05), '.0g')
```pycon
>>> format(9.995074823339339e-05, '.0g')
'0.0001'
>>> format(mpmath.mpf(9.995074823339339e-05), '.0g')
'1e-04'
```
see https://github.com/mpmath/mpmath/actions/runs/11398339589/job/31715210493

Pull request description:
Correct 'g' format type handling
Closes #880

Patch:
diff --git a/mpmath/libmp/libmpf.py b/mpmath/libmp/libmpf.py
index fe2d3c8..18399b1 100644
--- a/mpmath/libmp/libmpf.py
+++ b/mpmath/libmp/libmpf.py
@@ -1098,7 +1098,7 @@ def to_digits_exp(s, dps, base=10):
     return sign, digits, exponent
 
 def round_digits(sign, digits, dps, base, rounding=round_nearest,
-                 hack0=False):
+                 fixed=False):
     '''
     Returns the rounded digits, and the number of places the decimal point was
     shifted.
@@ -1171,7 +1171,7 @@ def round_digits(sign, digits, dps, base, rounding=round_nearest,
                 '0' * (dps - i - 1)
         else:
             # When rounding up 0.9999... in fixed format, we lose one dps.
-            digits = '1' + '0' * (dps - (0 if hack0 else 1))
+            digits = '1' + '0' * (dps - (0 if fixed else 1))
             exponent += 1
     else:
         digits = digits[:dps]
@@ -1495,7 +1495,6 @@ def format_fixed(s,
                  base=10,
                  alternate=False,
                  no_neg_0=False,
-                 hack0=True,
                  rounding=round_nearest):
     '''
     Format a number into fixed point.
@@ -1528,7 +1527,7 @@ def format_fixed(s,
         if no_neg_0:
             sign = '' if sign_spec == '-' else sign_spec
     else:
-        digits, exp_add = round_digits(s[0], digits, dps, base, rounding, hack0)
+        digits, exp_add = round_digits(s[0], digits, dps, base, rounding, True)
         exponent += exp_add
 
         if all(_ == '0' for _ in digits) and no_neg_0:
@@ -1686,7 +1685,6 @@ def format_binary(s,
 
 
 def format_digits(num, format_dict, prec):
-    hack0 = True
     capitalize = False
     if format_dict['type'] in list('AFGE'):
         capitalize = True
@@ -1711,8 +1709,6 @@ def format_digits(num, format_dict, prec):
     rounding = format_dict['rounding']
 
     if not fmt_type or fmt_type == 'g':
-        if fmt_type == 'g':
-            hack0 = False
         if not format_dict['alternate']:
             strip_zeros = True
             if fmt_type == 'g':
@@ -1724,7 +1720,7 @@ def format_digits(num, format_dict, prec):
             precision = 1
 
         _, tdigits, exp = to_digits_exp(num, max(53/blog2_10, precision), 10)
-        if not fmt_type and num[1]:
+        if num[1]:
             _, exp_add = round_digits(num, tdigits, precision, 10, rounding)
             exp += exp_add
 
@@ -1791,7 +1787,6 @@ def format_digits(num, format_dict, prec):
                 base=10,
                 alternate=format_dict['alternate'],
                 no_neg_0=format_dict['no_neg_0'],
-                hack0=hack0,
                 rounding=rounding
                 )
         if not fmt_type:


Test patch:
diff --git a/mpmath/tests/test_format.py b/mpmath/tests/test_format.py
index 9d96d48..7036988 100644
--- a/mpmath/tests/test_format.py
+++ b/mpmath/tests/test_format.py
@@ -467,6 +467,7 @@ def test_mpf_fmt_cpython():
        st.floats(allow_nan=True,
                  allow_infinity=True,
                  allow_subnormal=True))
+@example(fmt='.0g', x=9.995074823339339e-05)  # issue 880
 def test_mpf_floats_bulk(fmt, x):
     '''
     These are additional random tests that check that mp.mpf and fp.mpf yield
@@ -476,6 +477,8 @@ def test_mpf_floats_bulk(fmt, x):
     if not x and math.copysign(1, x) == -1:
         return  # skip negative zero
     spec = read_format_spec(fmt)
+    if sys.version_info < (3, 9) and spec['type'] in ('g', 'G'):
+        pytest.skip("CPython has incompatible g/G formatting in 3.8 and 3.9+")
     if not spec['type'] and spec['precision'] < 0 and math.isfinite(x):
         # The mpmath could choose a different decimal
         # representative (wrt CPython) for same binary
@@ -497,6 +500,8 @@ def test_mpc_complexes(fmt, z):
             or (not z.imag and math.copysign(1, z.imag) == -1)):
         return  # skip negative zero
     spec = read_format_spec(fmt)
+    if sys.version_info < (3, 9) and spec['type'] in ('g', 'G'):
+        pytest.skip("CPython has incompatible g/G formatting in 3.8 and 3.9+")
     if spec['precision'] < 0 and any(math.isfinite(_) for _ in [z.real, z.imag]):
         # The mpmath could choose a different decimal
         # representative (wrt CPython) for same binary