https://gcc.gnu.org/g:4aebd0c595b8c979facbb0e9c7dbadf67d18ff3c

commit r16-6268-g4aebd0c595b8c979facbb0e9c7dbadf67d18ff3c
Author: Jakub Jelinek <[email protected]>
Date:   Fri Dec 19 11:04:50 2025 +0100

    ++: Fix up cp_compare_floating_point_conversion_ranks for dfp [PR122834]
    
    The following testcase ICEs in cp_compare_floating_point_conversion_ranks,
    when it is called on one extended floating point type (right now always
    a binary floating point type) and a decimal floating point type (which
    we currently handle as neither standard nor extended floating point type,
    similarly to e.g. __float128 and similar types).
    There is an assertion that fails in that case.
    When no extended floating point types are involved, e.g. common type
    choice is quite arbitrary if TYPE_PRECISION is the same, e.g.
    auto a = 0.0DL + 1.0Q;
    auto b = 1.0Q + 0.0DL;
    chooses the first type in both cases, so decltype (0.0DL) in the first
    case and __float128 in the second case.
    Now, when one type is extended floating point, I think we should follow
    the C++23 rules, which say that conversion ranks are unordered if the
    set of the values of both types are neither proper subsets nor supersets
    of the other, which is I think the case of binary vs. decimal,
    e.g. 0.3D{F,D,L} is not exactly representable in any binary floating point
    format and I thought e.g. (1.0FNN + __FLTNN_EPSILON__) * __FLTNN_MIN__
    is not representable in any decimal floating point.  At least, for
    _Float32, it needs 112 decimal digits to represent it exactly
    
0.00000000000000000000000000000000000001175494490952133940450443629595204006810278684798281709160328881985245648433835441437622648663818836212158203125
    and _Decimal128 has only 34 significant digits, for _Float64
    that is already 767 significant digits etc.
    
    Though, _Float16 is a different case.
    The following helper program:
    
    int
    main ()
    {
      char buf[256], *p, *q;
      size_t l, ml = 0;
      {
        union { _Float16 x; unsigned short y; } u, v;
        for (int i = 0; i < 0x7c00; ++i)
          {
            u.y = i;
            _Float32 x = u.x;
            strfromf32 (buf, 255, "%.254f", x);
            for (p = buf; *p == '0' || *p == '.'; ++p)
              ;
            if (*p == '\0')
              continue;
            for (q = strchr (p, '\0') - 1; *q == '0' || *q == '.'; --q)
              ;
            q[1] = '\0';
            l = strlen (p);
            if (strchr (p, '.'))
              --l;
            if (ml < l)
              ml = l;
          }
      }
      printf ("%zd\n", ml);
      ml = 0;
      {
        union { __bf16 x; unsigned short y; } u, v;
        for (int i = 0; i < 0x7f80; ++i)
          {
            u.y = i;
            _Float32 x = u.x;
            strfromf32 (buf, 255, "%.254f", x);
            for (p = buf; *p == '0' || *p == '.'; ++p)
              ;
            if (*p == '\0')
              continue;
            for (q = strchr (p, '\0') - 1; *q == '0' || *q == '.'; --q)
              ;
            q[1] = '\0';
            l = strlen (p);
            if (strchr (p, '.'))
              --l;
            if (ml < l)
              ml = l;
          }
      }
      printf ("%zd\n", ml);
    }
    
    prints
    21
    96
    As _Decimal32 has 7 and _Decimal64 16 decimal digits, I think neither
    _Float16 nor decltype (0.0bf16) is proper subset of values of those types,
    but as _Decimal128 has 34 decimal digits, I'd say _Float16 is a proper
    subset of _Decimal128 while decltype (0.0bf16) is not.
    Example of the 21 decimal digits for _Float16 is
    0x1.a3cp-14f16 (0x68f u.y), which is exactly 0.000100076198577880859375
    
    2025-12-18  Jakub Jelinek  <[email protected]>
    
            PR c++/122834
            * typeck.cc (cp_compare_floating_point_conversion_ranks): Return
            3 if fmt2->b is 10 except for _Float16 vs. _Decimal128, in that
            case return -2.
    
            * g++.dg/dfp/pr122834-1.C: New test.
            * g++.dg/dfp/pr122834-2.C: New test.

Diff:
---
 gcc/cp/typeck.cc                      | 25 ++++++++++++++++++++++++-
 gcc/testsuite/g++.dg/dfp/pr122834-1.C | 17 +++++++++++++++++
 gcc/testsuite/g++.dg/dfp/pr122834-2.C | 19 +++++++++++++++++++
 3 files changed, 60 insertions(+), 1 deletion(-)

diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
index 65e08eaa645c..39aecb4b2416 100644
--- a/gcc/cp/typeck.cc
+++ b/gcc/cp/typeck.cc
@@ -305,7 +305,30 @@ cp_compare_floating_point_conversion_ranks (tree t1, tree 
t2)
 
   const struct real_format *fmt1 = REAL_MODE_FORMAT (TYPE_MODE (t1));
   const struct real_format *fmt2 = REAL_MODE_FORMAT (TYPE_MODE (t2));
-  gcc_assert (fmt1->b == 2 && fmt2->b == 2);
+  /* Currently, extended floating point types are only binary, and
+     they never have a proper subset or superset of values with
+     decimal floating point types except for the _Float16 vs. _Decimal128
+     pair, so return 3 for unordered conversion ranks.  */
+  gcc_assert (fmt1->b == 2);
+  if (fmt2->b == 10)
+    {
+      /* _Float16 needs at most 21 decimal digits (e.g.
+        0x1.a3cp-14f16 is exactly 0.000100076198577880859375DL),
+        so it is not a proper subset of _Decimal64 but is subset
+        of _Decimal128.  While std::bfloat16_t needs at most 96
+        decimal digits, so even _Decimal128 doesn't cover it.
+        _Float32 has at least one value which needs 112 decimal
+        digits, _Float64 at least 767 decimal digits.  */
+      if (fmt1->emin == -13
+         && fmt1->emax == 16
+         && fmt1->p == 11
+         && fmt2->emin == -6142
+         && fmt2->emax == 6145
+         && fmt2->p == 34)
+       return -2;
+      return 3;
+    }
+  gcc_assert (fmt2->b == 2);
   /* For {ibm,mips}_extended_format formats, the type has variable
      precision up to ~2150 bits when the first double is around maximum
      representable double and second double is subnormal minimum.
diff --git a/gcc/testsuite/g++.dg/dfp/pr122834-1.C 
b/gcc/testsuite/g++.dg/dfp/pr122834-1.C
new file mode 100644
index 000000000000..e73611dfabad
--- /dev/null
+++ b/gcc/testsuite/g++.dg/dfp/pr122834-1.C
@@ -0,0 +1,17 @@
+// PR c++/122834
+// { dg-do compile { target { c++11 && float128 } } }
+// { dg-options "" }
+// { dg-add-options float128 }
+
+typedef decltype (0.0DL) A;
+typedef _Float128 B;
+void bar (A);                  // { dg-message "initializing argument 1 of" }
+
+void
+foo (B x)
+{
+  bar (x);                     // { dg-warning "with unordered conversion 
rank" }
+}
+
+auto a = 0.0DL + 1.0F128;      // { dg-error "invalid operands to binary \\\+" 
}
+auto b = 1.0F128 + 0.0DL;      // { dg-error "invalid operands to binary \\\+" 
}
diff --git a/gcc/testsuite/g++.dg/dfp/pr122834-2.C 
b/gcc/testsuite/g++.dg/dfp/pr122834-2.C
new file mode 100644
index 000000000000..2f543e3464be
--- /dev/null
+++ b/gcc/testsuite/g++.dg/dfp/pr122834-2.C
@@ -0,0 +1,19 @@
+// PR c++/122834
+// { dg-do compile { target { c++11 && float16 } } }
+// { dg-options "" }
+// { dg-add-options float16 }
+
+typedef decltype (0.0DL) A;
+typedef _Float16 B;
+void bar (A);
+
+void
+foo (B x)
+{
+  bar (x);
+}
+
+auto a = 0.0DL + 1.0F16;
+auto b = 1.0F16 + 0.0DL;
+static_assert (__is_same_as (decltype (0.0DL + 1.0F16), decltype (0.0DL)));
+static_assert (__is_same_as (decltype (1.0F16 + 0.0DL), decltype (0.0DL)));

Reply via email to