https://github.com/conrade-ctc updated 
https://github.com/llvm/llvm-project/pull/218471

>From 997312def19e859ffd207d0d6b3766441b70cfa7 Mon Sep 17 00:00:00 2001
From: Emery Conrad <[email protected]>
Date: Mon, 24 Aug 2026 16:20:28 -0500
Subject: [PATCH] [clang][APFloat] Print floating-point literals in the
 shortest round-trip form

StmtPrinter printed a FloatingLiteral at the maximum round-trip
precision, so 3.14 printed as 3.1400000000000001. Add
APFloat::toStringShortest and call it from PrintFloatingLiteral.
It computes the shortest digit count from the value's rounding interval
(Ryu's formulation with a Dragonbox-style grid anchor) in exact APInt
arithmetic, so every IEEEFloat semantics works, then formats with the
existing toString at that count and verifies the round trip.
PPCDoubleDouble keeps a plain probe from precision 1.

Two cases keep the natural-precision form. An integer in fixed notation
has exact digits; a shorter form would only switch it to scientific
notation (100.0 stays "100", not "1.0E+2"). The largest value of a format
with neither infinity nor NaN parses back from any larger string by
saturation, which is not a round trip.

The verify step is a new APFloat::toStringRoundTrip; the MLIR and llubi
printers hand-rolled the same bitwise check and now call it, with no
output change. The IR AsmWriter is unchanged: its numeric comparison
hides a ppc_fp128 low-component sign difference (issue #224351) that a
bitwise check would send to the hex path.

Updated three tests that pinned the old maximum-precision output. The
-ast-print test covers hex literals, integers, boundary values, and
long double as x87, IEEE quad, and double-double. Unit tests pin
corner cases from the Ryu test suite for double and float, the 8-bit
and smaller formats, and check every value of every format up to 16
bits against a brute-force reference.

Co-developed-with-the-help-of: Claude Code (Fable 5.1, human in the loop)
---
 clang/docs/ReleaseNotes.md                  |   4 +
 clang/lib/AST/StmtPrinter.cpp               |   2 +-
 clang/test/AST/ast-print-float-shortest.cpp | 134 ++++++++++
 clang/test/PCH/floating-literal.c           |   4 +-
 clang/test/PCH/objc_literals.m              |   6 +-
 clang/test/Sema/x86_64-linux-android.c      |   2 +-
 llvm/include/llvm/ADT/APFloat.h             |  15 ++
 llvm/lib/Support/APFloat.cpp                | 175 +++++++++++++
 llvm/tools/llubi/lib/Value.cpp              |   6 +-
 llvm/unittests/ADT/APFloatTest.cpp          | 261 ++++++++++++++++++++
 mlir/lib/IR/AsmPrinter.cpp                  |  11 +-
 11 files changed, 605 insertions(+), 15 deletions(-)
 create mode 100644 clang/test/AST/ast-print-float-shortest.cpp

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 9a69d1888b1bfc..0ba52429092ed1 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -159,6 +159,10 @@ features cannot lower the translation-unit ABI level;
 
 ### AST Dumping Potentially Breaking Changes
 
+- The AST printer (`-ast-print`) now prints a floating-point literal as the
+  shortest decimal form that round-trips to the same value. For example,
+  `3.14` printed as `3.1400000000000001` before and prints as `3.14` now.
+
 ### Clang Frontend Potentially Breaking Changes
 
 - Templight support has been removed.
diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp
index 4f184868659f48..5e1d45e0f2ff02 100644
--- a/clang/lib/AST/StmtPrinter.cpp
+++ b/clang/lib/AST/StmtPrinter.cpp
@@ -1603,7 +1603,7 @@ void 
StmtPrinter::VisitFixedPointLiteral(FixedPointLiteral *Node) {
 static void PrintFloatingLiteral(raw_ostream &OS, FloatingLiteral *Node,
                                  bool PrintSuffix) {
   SmallString<16> Str;
-  Node->getValue().toString(Str);
+  Node->getValue().toStringShortest(Str);
   OS << Str;
   if (Str.find_first_not_of("-0123456789") == StringRef::npos)
     OS << '.'; // Trailing dot in order to separate from ints.
diff --git a/clang/test/AST/ast-print-float-shortest.cpp 
b/clang/test/AST/ast-print-float-shortest.cpp
new file mode 100644
index 00000000000000..4cc328bd5df40b
--- /dev/null
+++ b/clang/test/AST/ast-print-float-shortest.cpp
@@ -0,0 +1,134 @@
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -ast-print %s | FileCheck %s 
--check-prefixes=CHECK,F16,X87,Q128
+// RUN: %clang_cc1 -triple aarch64-linux-gnu -ast-print %s | FileCheck %s 
--check-prefixes=CHECK,F16,QUAD
+// RUN: %clang_cc1 -triple powerpc64-linux-gnu -ast-print %s | FileCheck %s 
--check-prefixes=CHECK,PPC
+
+// Floating literals print as the shortest decimal form that round-trips to
+// the same value in the literal's type.
+
+double d1 = 3.14;
+// CHECK: double d1 = 3.14;
+double d2 = 4.0;
+// CHECK: double d2 = 4.;
+double d3 = 0.5;
+// CHECK: double d3 = 0.5;
+double d4 = 1e10;
+// CHECK: double d4 = 1.0E+10;
+double d5 = 1e-300;
+// CHECK: double d5 = 1.0E-300;
+double d6 = 0.1;
+// CHECK: double d6 = 0.1;
+// The smallest double denormal: the shortest round-trip form is 5E-324.
+double d7 = 4.9406564584124654e-324;
+// CHECK: double d7 = 5.0E-324;
+// DBL_MAX needs all 17 significant digits; nothing shorter round-trips.
+double d8 = 1.7976931348623157e+308;
+// CHECK: double d8 = 1.7976931348623157E+308;
+double d9 = -3.14;
+// CHECK: double d9 = -3.14;
+
+// A hex float keeps its value, printed in decimal.
+double h1 = 0x10.1p0;
+// CHECK: double h1 = 16.0625;
+double h2 = 0x1p-1074;
+// CHECK: double h2 = 5.0E-324;
+
+// Integers keep their digits; the shortest form would only switch them to
+// scientific notation. More than three trailing zeros still do, as before.
+double i1 = 10.0;
+// CHECK: double i1 = 10.;
+double i2 = 100.0;
+// CHECK: double i2 = 100.;
+double i3 = 1200.0;
+// CHECK: double i3 = 1200.;
+double i4 = 120000.0;
+// CHECK: double i4 = 1.2E+5;
+// 2^53 + 1 rounds to 2^53.
+double i5 = 9007199254740993.0;
+// CHECK: double i5 = 9007199254740992.;
+// 1e23 is not exactly representable; 1E23 is still the shortest form.
+double i6 = 1e23;
+// CHECK: double i6 = 1.0E+23;
+
+// The smallest normal and the largest denormal double.
+double b1 = 2.2250738585072014e-308;
+// CHECK: double b1 = 2.2250738585072014E-308;
+double b2 = 2.2250738585072009e-308;
+// CHECK: double b2 = 2.225073858507201E-308;
+// 0.1 + 0.2 needs all 17 digits.
+double b3 = 0.30000000000000004;
+// CHECK: double b3 = 0.30000000000000004;
+double b4 = 0.0001;
+// CHECK: double b4 = 1.0E-4;
+// A significand that looks like a power of five (Ryu's LooksLikePow5).
+double b5 = 5.764607523034235e39;
+// CHECK: double b5 = 5.764607523034235E+39;
+
+float f1 = 3.14f;
+// CHECK: float f1 = 3.14F;
+float f2 = 0.1f;
+// CHECK: float f2 = 0.1F;
+float f3 = 0x1.8p3f;
+// CHECK: float f3 = 12.F;
+float f4 = 0x10.1p0f;
+// CHECK: float f4 = 16.0625F;
+float f5 = 100.0f;
+// CHECK: float f5 = 100.F;
+// FLT_MAX and the smallest float denormal.
+float f6 = 3.4028235e38f;
+// CHECK: float f6 = 3.4028235E+38F;
+float f7 = 1.4e-45f;
+// CHECK: float f7 = 1.0E-45F;
+// 2^24 + 1 rounds to 2^24.
+float f8 = 16777217.0f;
+// CHECK: float f8 = 16777216.F;
+// The upper bound of the rounding interval is a round-trip form when the
+// significand is even (Ryu's BoundaryRoundEven).
+float f9 = 3.4366717e10f;
+// CHECK: float f9 = 3.436672E+10F;
+
+#ifdef __FLT16_MANT_DIG__
+_Float16 h16_1 = 0.1f16;
+// F16: _Float16 h16_1 = 0.1F16;
+_Float16 h16_2 = 65504.0f16;
+// F16: _Float16 h16_2 = 65504.F16;
+// 16.0625 is exact in _Float16, but 16.06 already round-trips.
+_Float16 h16_3 = 0x10.1p0f16;
+// F16: _Float16 h16_3 = 16.06F16;
+// The smallest _Float16 denormal.
+_Float16 h16_4 = 5.9604644775390625e-8f16;
+// F16: _Float16 h16_4 = 6.0E-8F16;
+#endif
+
+// long double: x87 80-bit on x86_64, IEEE quad on AArch64, IBM double-double
+// on PowerPC.
+long double ld1 = 0.1L;
+// CHECK: long double ld1 = 0.1L;
+long double ld2 = 3.14L;
+// CHECK: long double ld2 = 3.14L;
+long double ld3 = 0x10.1p0L;
+// CHECK: long double ld3 = 16.0625L;
+long double ld4 = 100.0L;
+// CHECK: long double ld4 = 100.L;
+// 1 + 2^-52 is exact in every long double format; the digit count follows
+// the format's precision.
+long double ld5 = 1.0000000000000002220446049250313L;
+// X87: long double ld5 = 1.000000000000000222L;
+// QUAD: long double ld5 = 1.0000000000000002220446049250313L;
+// PPC: long double ld5 = 1.0000000000000002220446049250313L;
+// The smallest double denormal is a denormal only in double-double.
+long double ld6 = 4.9406564584124654e-324L;
+// X87: long double ld6 = 4.9406564584124654E-324L;
+// QUAD: long double ld6 = 4.9406564584124654E-324L;
+// PPC: long double ld6 = 5.0E-324L;
+// 1 + 1e-37 is below every format's precision.
+long double ld7 = 1.0000000000000000000000000000000000001L;
+// CHECK: long double ld7 = 1.L;
+
+#ifdef __SIZEOF_FLOAT128__
+__float128 q1 = 0.1q;
+// Q128: __float128 q1 = 0.1Q;
+__float128 q2 = 0x10.1p0q;
+// Q128: __float128 q2 = 16.0625Q;
+__float128 q3 = 100.0q;
+// Q128: __float128 q3 = 100.Q;
+#endif
diff --git a/clang/test/PCH/floating-literal.c 
b/clang/test/PCH/floating-literal.c
index b5ff6fe84b88ce..4590b1990b79de 100644
--- a/clang/test/PCH/floating-literal.c
+++ b/clang/test/PCH/floating-literal.c
@@ -8,12 +8,12 @@
 // targets with 128-bit IEEE long doubles.
 
 long double foo = 1.0E4000L;
-// CHECK: long double foo = 1.00000000000000000000000000000000004E+4000L;
+// CHECK: long double foo = 1.0E+4000L;
 
 // Just as well check the others are still sane while we're here...
 
 double bar = 1.0E300;
-// CHECK: double bar = 1.0000000000000001E+300;
+// CHECK: double bar = 1.0E+300;
 
 float wibble = 1.0E40;
 // CHECK: float wibble = 1.0E+40;
diff --git a/clang/test/PCH/objc_literals.m b/clang/test/PCH/objc_literals.m
index 16ed6aeb9b9339..ede3358e2fe698 100644
--- a/clang/test/PCH/objc_literals.m
+++ b/clang/test/PCH/objc_literals.m
@@ -45,18 +45,18 @@ static inline void test_numeric_literals(void) {
   // CHECK-PRINT: id intlit = @17
   // CHECK-IR: {{call.*17}}
   id intlit = @17;
-  // CHECK-PRINT: id floatlit = @17.449999999999999
+  // CHECK-PRINT: id floatlit = @17.45
   // CHECK-IR: {{call.*1.745}}
   id floatlit = @17.45;
 }
 
 static inline void test_array_literals(void) {
-  // CHECK-PRINT: id arraylit = @[ @17, @17.449999999999999
+  // CHECK-PRINT: id arraylit = @[ @17, @17.45
   id arraylit = @[@17, @17.45];
 }
 
 static inline void test_dictionary_literals(void) {
-  // CHECK-PRINT: id dictlit = @{ @17 : {{@17.449999999999999[^,]*}}, @"hello" 
: @"world" };
+  // CHECK-PRINT: id dictlit = @{ @17 : {{@17.45[^,]*}}, @"hello" : @"world" };
   id dictlit = @{@17 : @17.45, @"hello" : @"world" };
 }
 
diff --git a/clang/test/Sema/x86_64-linux-android.c 
b/clang/test/Sema/x86_64-linux-android.c
index 252570579f6076..75158dc066dc11 100644
--- a/clang/test/Sema/x86_64-linux-android.c
+++ b/clang/test/Sema/x86_64-linux-android.c
@@ -7,4 +7,4 @@ extern int a1_i[__alignof(long double) == 16 ? 1 : -1];
 // Verify that long double is 128 bit IEEEquad
 
 long double foo = 1.0E4000L;
-// CHECK: long double foo = 1.00000000000000000000000000000000004E+4000L;
+// CHECK: long double foo = 1.0E+4000L;
diff --git a/llvm/include/llvm/ADT/APFloat.h b/llvm/include/llvm/ADT/APFloat.h
index 9b903fc888b4cf..6655720bee27a6 100644
--- a/llvm/include/llvm/ADT/APFloat.h
+++ b/llvm/include/llvm/ADT/APFloat.h
@@ -1623,6 +1623,21 @@ class APFloat : public APFloatBase {
         toString(Str, FormatPrecision, FormatMaxPadding, TruncateZero));
   }
 
+  /// Format the value as toString does and return true if the string parses
+  /// back bitwise equal. Non-finite spellings ("+Inf", "NaN") parse back
+  /// here but may not fit a caller's grammar.
+  LLVM_ABI bool toStringRoundTrip(SmallVectorImpl<char> &Str,
+                                  unsigned FormatPrecision = 0,
+                                  unsigned FormatMaxPadding = 3,
+                                  bool TruncateZero = true) const;
+
+  /// Like toString, but picks the smallest FormatPrecision whose output
+  /// parses back bitwise equal: "3.14", not "3.1400000000000001". An integer
+  /// in fixed notation keeps its digits: "100", not "1.0E+2".
+  LLVM_ABI void toStringShortest(SmallVectorImpl<char> &Str,
+                                 unsigned FormatMaxPadding = 3,
+                                 bool TruncateZero = true) const;
+
   LLVM_ABI void print(raw_ostream &) const;
 
 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
diff --git a/llvm/lib/Support/APFloat.cpp b/llvm/lib/Support/APFloat.cpp
index 1435c35efc2ab5..a2848fc46d90d5 100644
--- a/llvm/lib/Support/APFloat.cpp
+++ b/llvm/lib/Support/APFloat.cpp
@@ -18,6 +18,7 @@
 #include "llvm/ADT/FoldingSet.h"
 #include "llvm/ADT/Hashing.h"
 #include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/StringRef.h"
@@ -6071,6 +6072,180 @@ APFloat APFloat::getAllOnesValue(const fltSemantics 
&Semantics) {
   return APFloat(Semantics, APInt::getAllOnes(Semantics.sizeInBits));
 }
 
+bool APFloat::toStringRoundTrip(SmallVectorImpl<char> &Str,
+                                unsigned FormatPrecision,
+                                unsigned FormatMaxPadding,
+                                bool TruncateZero) const {
+  SmallString<32> Buf;
+  toString(Buf, FormatPrecision, FormatMaxPadding, TruncateZero);
+  Str.append(Buf.begin(), Buf.end());
+  APFloat Parsed(getSemantics());
+  Expected<opStatus> Status =
+      Parsed.convertFromString(Buf, rmNearestTiesToEven);
+  if (!Status) {
+    consumeError(Status.takeError());
+    return false;
+  }
+  return Parsed.bitwiseIsEqual(*this);
+}
+
+/// 5^Exp zero-extended to \p Width bits. Each step multiplies by one 64-bit
+/// word, a single linear pass. Square-and-multiply needs full-width APInt
+/// products, which are quadratic and measure slower at these widths.
+static APInt pow5AsAPInt(unsigned Exp, unsigned Width) {
+  APInt Result(Width, 1);
+  for (; Exp >= 27; Exp -= 27)
+    Result *= UINT64_C(7450580596923828125); // 5^27, the largest 64-bit power.
+  uint64_t Tail = 1;
+  while (Exp--)
+    Tail *= 5;
+  Result *= Tail;
+  return Result;
+}
+
+/// Minimum number of significant decimal digits a string needs to parse back
+/// (round-to-nearest-even) to exactly \p Val. Val must be finite, non-zero,
+/// and use the IEEEFloat layout.
+///
+/// A string parses back to Val exactly when its value rounds to Val, so the
+/// answer is the fewest significant digits of any decimal in Val's rounding
+/// interval. The interval follows Ryu (Adams, PLDI 2018,
+/// https://doi.org/10.1145/3192366.3192369); the shortest decimal in it is
+/// found as in Dragonbox (Jeon, github.com/jk-jeon/dragonbox, after
+/// Giulietti's Schubfach): anchor on the coarsest power-of-ten grid the
+/// interval width guarantees to hit, then walk the trailing zeros. Exact
+/// APInt arithmetic replaces the per-format power tables, so every IEEEFloat
+/// semantics works.
+static unsigned shortestDigitCount(const APFloat &Val) {
+  const fltSemantics &Sem = Val.getSemantics();
+  const unsigned Prec = APFloat::semanticsPrecision(Sem);
+  const int MinExp = APFloat::semanticsMinExponent(Sem);
+
+  // Exact integer significand M and exponent Q with |Val| == M * 2^Q.
+  const APFloat AbsVal = abs(Val);
+  const int Q = std::max(ilogb(AbsVal), MinExp) - int(Prec - 1);
+  APFloat Wide = AbsVal;
+  if (APFloat::semanticsSizeInBits(Sem) <= 32) {
+    // Tiny formats cannot hold their own significand as an integer (e.g.
+    // Float6E2M3FN); go through IEEEdouble, an exact superset of them all.
+    bool LosesInfo = false;
+    Wide.convert(APFloat::IEEEdouble(), APFloat::rmNearestTiesToEven,
+                 &LosesInfo);
+    assert(!LosesInfo && "widening conversion is exact");
+  }
+  bool IsExact = false;
+  APSInt M(Prec + 2, /*isUnsigned=*/true);
+  scalbn(Wide, -Q, APFloat::rmNearestTiesToEven)
+      .convertToInteger(M, APFloat::rmNearestTiesToEven, &IsExact);
+  assert(IsExact && "significand extraction is exact");
+
+  // The gap below halves when the significand is a power of two above the
+  // minimum exponent (Ryu's mmShift). The interval is (Low, High) * 2^E2;
+  // its bounds parse back to Val only when the significand is even (a
+  // halfway decimal ties to the even neighbor).
+  const bool HalfBelow = M.isPowerOf2() && ilogb(AbsVal) > MinExp;
+  const bool Inclusive = !M[0];
+  const int E2 = Q - (HalfBelow ? 2 : 1);
+  // Width holds every value below: the bounds are under 2^(Prec + 2); scaling
+  // by 2^E2 adds E2 bits, and by 5^-E2 at most floor(-E2 * log2(5)) + 1 bits
+  // (2322/1000 is just above log2(5) = 2.32193); the grid search forms
+  // P10 * 10 < 16 * High, which adds 4 more.
+  const unsigned ScaleBits =
+      E2 >= 0 ? unsigned(E2) : unsigned((uint64_t(-E2) * 2322) / 1000) + 1;
+  const unsigned Width = Prec + 2 + ScaleBits + 4;
+  APInt MW = M.zext(Width);
+  APInt Low = HalfBelow ? (MW << 2) - 1 : (MW << 1) - 1;
+  APInt High = HalfBelow ? (MW << 2) + 2 : (MW << 1) + 1;
+
+  // Scale the bounds to integers by a shared power of ten; every candidate
+  // keeps its significant digit count.
+  if (E2 >= 0) {
+    Low <<= unsigned(E2);
+    High <<= unsigned(E2);
+  } else {
+    const APInt P5 = pow5AsAPInt(unsigned(-E2), Width);
+    Low *= P5;
+    High *= P5;
+  }
+
+  // Anchor grid: the largest T0 with 10^T0 <= the interval width, so
+  // multiples of 10^T0 cannot all miss. 30102/100000 underestimates
+  // log10(2), so the estimate never overshoots and trails T0 by at most two.
+  const APInt IntervalWidth = High - Low;
+  unsigned T0 =
+      unsigned((uint64_t(IntervalWidth.getActiveBits() - 1) * 30102) / 100000);
+  APInt P10 = pow5AsAPInt(T0, Width).shl(T0);
+  while ((P10 * 10).ule(IntervalWidth)) {
+    P10 *= 10;
+    ++T0;
+  }
+
+  // Candidate multiples of 10^T0 in the interval: CMin..CMax, at most ten of
+  // them. Open bounds can eat a lone candidate; one grid finer cannot miss.
+  APInt CMin(Width, 0), CMax(Width, 0);
+  auto FindCandidates = [&]() {
+    APInt Quo(Width, 0), Rem(Width, 0);
+    APInt::udivrem(Low, P10, Quo, Rem);
+    CMin = (Inclusive && Rem.isZero()) ? Quo : Quo + 1;
+    APInt::udivrem(High, P10, Quo, Rem);
+    CMax = (!Inclusive && Rem.isZero()) ? Quo - 1 : Quo;
+  };
+  FindCandidates();
+  while (CMin.ugt(CMax)) {
+    P10 = P10.udiv(10);
+    FindCandidates();
+  }
+
+  // Step to coarser grids while the range still holds a multiple of the next
+  // power of ten. The candidates are small, so these divisions are cheap.
+  while (true) {
+    const APInt CMax10 = CMax.udiv(10);
+    if ((CMax10 * 10).ult(CMin))
+      break;
+    CMin = (CMin + 9).udiv(10);
+    CMax = CMax10;
+  }
+  unsigned Digits = 0;
+  for (; !CMin.isZero(); ++Digits)
+    CMin = CMin.udiv(10);
+  return Digits;
+}
+
+void APFloat::toStringShortest(SmallVectorImpl<char> &Str,
+                               unsigned FormatMaxPadding,
+                               bool TruncateZero) const {
+  SmallString<32> Best;
+  toString(Best, /*FormatPrecision=*/0, FormatMaxPadding, TruncateZero);
+  // An integer printed in fixed notation has exact digits; a shorter form
+  // only trades them for scientific notation ("1.0E+2" for 100). Keep it.
+  const bool KeepIntegerDigits =
+      StringRef(Best).find_first_of(".Ee") == StringRef::npos;
+  // A format with neither infinity nor NaN parses every larger string as its
+  // largest value. That is saturation, not a round trip; keep the natural
+  // form ("7.5", not "8", for the largest Float6E2M3FN).
+  const bool Saturates = !semanticsHasInf(getSemantics()) &&
+                         !semanticsHasNaN(getSemantics()) &&
+                         abs(*this).bitwiseIsEqual(getLargest(getSemantics()));
+  if (isFiniteNonZero() && !KeepIntegerDigits && !Saturates) {
+    // Probe below the conservative natural precision (toStringImpl's FIXME).
+    // No precision below shortestDigitCount can parse back equal, so start
+    // there; the result matches probing from 1. DoubleAPFloat layouts keep
+    // the full probe.
+    unsigned Precision =
+        usesLayout<IEEEFloat>(getSemantics()) ? shortestDigitCount(*this) : 1;
+    for (unsigned MaxPrecision = Best.size(); Precision < MaxPrecision;
+         ++Precision) {
+      SmallString<32> Candidate;
+      if (toStringRoundTrip(Candidate, Precision, FormatMaxPadding,
+                            TruncateZero)) {
+        Best = Candidate;
+        break;
+      }
+    }
+  }
+  Str.append(Best.begin(), Best.end());
+}
+
 void APFloat::print(raw_ostream &OS) const {
   SmallVector<char, 16> Buffer;
   toString(Buffer);
diff --git a/llvm/tools/llubi/lib/Value.cpp b/llvm/tools/llubi/lib/Value.cpp
index 0993441abf22d2..81445fd9a777da 100644
--- a/llvm/tools/llubi/lib/Value.cpp
+++ b/llvm/tools/llubi/lib/Value.cpp
@@ -99,9 +99,9 @@ void AnyValue::print(Context &Ctx, raw_ostream &OS) const {
     // exponential notation if it is lossless, otherwise output it in
     // hexadecimal notation.
     SmallString<16> StrVal;
-    FloatVal.toString(StrVal, /*FormatPrecision=*/6, /*FormatMaxPadding=*/0,
-                      /*TruncateZero=*/false);
-    if (APFloat(FloatVal.getSemantics(), StrVal).bitwiseIsEqual(FloatVal)) {
+    if (FloatVal.toStringRoundTrip(StrVal, /*FormatPrecision=*/6,
+                                   /*FormatMaxPadding=*/0,
+                                   /*TruncateZero=*/false)) {
       OS << StrVal;
     } else {
       StrVal.clear();
diff --git a/llvm/unittests/ADT/APFloatTest.cpp 
b/llvm/unittests/ADT/APFloatTest.cpp
index 35393ef8ee20bf..06d23bed4edaa9 100644
--- a/llvm/unittests/ADT/APFloatTest.cpp
+++ b/llvm/unittests/ADT/APFloatTest.cpp
@@ -1842,6 +1842,267 @@ TEST(APFloatTest, toString) {
   }
 }
 
+static std::string convertToStringShortest(const APFloat &F, unsigned Pad = 3,
+                                           bool Tr = true) {
+  llvm::SmallVector<char, 100> Buffer;
+  F.toStringShortest(Buffer, Pad, Tr);
+  return std::string(Buffer.data(), Buffer.size());
+}
+
+TEST(APFloatTest, toStringRoundTrip) {
+  SmallString<32> Str;
+  // 6 digits: lossless for 3.14, lossy for pi/4; the string appends either 
way.
+  ASSERT_TRUE(APFloat(3.14).toStringRoundTrip(Str, 6, 0, false));
+  ASSERT_EQ("3.140000e+00", Str);
+  Str.clear();
+  ASSERT_FALSE(
+      APFloat(0.78539816339744830961).toStringRoundTrip(Str, 6, 0, false));
+  ASSERT_EQ("7.853980e-01", Str);
+  Str.clear();
+  // FormatPrecision = 0 (natural precision) always round-trips.
+  ASSERT_TRUE(APFloat(0.78539816339744830961).toStringRoundTrip(Str));
+  ASSERT_EQ("0.78539816339744828", Str);
+  Str.clear();
+  // "+Inf" parses back even though the IR and MLIR grammars reject it.
+  ASSERT_TRUE(APFloat::getInf(APFloat::IEEEdouble()).toStringRoundTrip(Str));
+  ASSERT_EQ("+Inf", Str);
+}
+
+TEST(APFloatTest, toStringShortest) {
+  ASSERT_EQ("3.14", convertToStringShortest(APFloat(3.14)));
+  ASSERT_EQ("-3.14", convertToStringShortest(APFloat(-3.14)));
+  ASSERT_EQ("4", convertToStringShortest(APFloat(4.0)));
+  ASSERT_EQ("0.5", convertToStringShortest(APFloat(0.5)));
+  ASSERT_EQ("0.1", convertToStringShortest(APFloat(0.1)));
+  ASSERT_EQ("873.1834", convertToStringShortest(APFloat(873.1834)));
+  ASSERT_EQ("1.0E+10", convertToStringShortest(APFloat(1e10)));
+  // A hex literal prints in decimal.
+  ASSERT_EQ("16.0625", convertToStringShortest(APFloat(0x10.1p0)));
+  // 0.1 + 0.2 and DBL_MAX need every natural-precision digit.
+  ASSERT_EQ("0.30000000000000004",
+            convertToStringShortest(APFloat(0.30000000000000004)));
+  ASSERT_EQ("1.7976931348623157E+308",
+            convertToStringShortest(APFloat(1.7976931348623157E+308)));
+  // The smallest denormal, the smallest normal, and the largest denormal.
+  ASSERT_EQ("5.0E-324",
+            convertToStringShortest(APFloat(4.9406564584124654e-324)));
+  ASSERT_EQ("2.2250738585072014E-308",
+            convertToStringShortest(APFloat(2.2250738585072014e-308)));
+  ASSERT_EQ("2.225073858507201E-308",
+            convertToStringShortest(APFloat(2.2250738585072009e-308)));
+  // 1e23 is not exactly representable; 1E23 is still the shortest form.
+  ASSERT_EQ("1.0E+23", convertToStringShortest(APFloat(1e23)));
+  // Significands that look like powers of five (Ryu's LooksLikePow5).
+  ASSERT_EQ("5.764607523034235E+39",
+            convertToStringShortest(
+                APFloat(APFloat::IEEEdouble(), APInt(64, 
0x4830F0CF064DD592))));
+  ASSERT_EQ("1.152921504606847E+40",
+            convertToStringShortest(
+                APFloat(APFloat::IEEEdouble(), APInt(64, 
0x4840F0CF064DD592))));
+  // Neighbors around 2^32 differ in the tenth digit.
+  ASSERT_EQ("4.294967295", convertToStringShortest(APFloat(4.294967295)));
+  ASSERT_EQ("4.294967296", convertToStringShortest(APFloat(4.294967296)));
+  ASSERT_EQ("4.294967297", convertToStringShortest(APFloat(4.294967297)));
+  // A power of two has a shorter gap below than above.
+  ASSERT_EQ("1.7800590868057611E-307",
+            convertToStringShortest(
+                APFloat(APFloat::IEEEdouble(), APInt(64, uint64_t(4) << 52))));
+  ASSERT_EQ("2.900835519859558E-216",
+            convertToStringShortest(APFloat(APFloat::IEEEdouble(),
+                                            APInt(64, uint64_t(307) << 52))));
+
+  // An integer in fixed notation keeps its digits; the shortest form would
+  // only trade them for scientific notation. More than FormatMaxPadding
+  // trailing zeros still switch to scientific notation, as in toString.
+  ASSERT_EQ("10", convertToStringShortest(APFloat(10.0)));
+  ASSERT_EQ("100", convertToStringShortest(APFloat(100.0)));
+  ASSERT_EQ("1200", convertToStringShortest(APFloat(1200.0)));
+  ASSERT_EQ("1.2E+5", convertToStringShortest(APFloat(120000.0)));
+  // 2^53 + 1 rounds to 2^53.
+  ASSERT_EQ("9007199254740992",
+            convertToStringShortest(APFloat(9007199254740993.0)));
+  // 17 digits fit the natural precision; 18 do not.
+  ASSERT_EQ("18014398509481984",
+            convertToStringShortest(APFloat(18014398509481984.0)));
+  ASSERT_EQ("1.2345678901234568E+17",
+            convertToStringShortest(APFloat(123456789012345680.0)));
+
+  // Single precision.
+  ASSERT_EQ("3.14", convertToStringShortest(APFloat(3.14f)));
+  ASSERT_EQ("0.1", convertToStringShortest(APFloat(0.1f)));
+  ASSERT_EQ("16.0625", convertToStringShortest(APFloat(0x10.1p0f)));
+  ASSERT_EQ("100", convertToStringShortest(APFloat(100.0f)));
+  ASSERT_EQ("3.4028235E+38", convertToStringShortest(APFloat(3.4028235e38f)));
+  ASSERT_EQ("1.0E-45", convertToStringShortest(APFloat(1.4e-45f)));
+  ASSERT_EQ("1.1754944E-38", convertToStringShortest(APFloat(1.1754944e-38f)));
+  // 2^24 + 1 rounds to 2^24.
+  ASSERT_EQ("16777216", convertToStringShortest(APFloat(16777217.0f)));
+  // The upper bound of the rounding interval parses back when the
+  // significand is even (Ryu's BoundaryRoundEven).
+  ASSERT_EQ("3.436672E+10", convertToStringShortest(APFloat(3.4366717e10f)));
+  ASSERT_EQ("1.5846086E+29", convertToStringShortest(APFloat(1.5846085e29f)));
+  ASSERT_EQ("6.7108864E+17",
+            convertToStringShortest(
+                APFloat(APFloat::IEEEsingle(), APInt(32, 0x5D1502F9))));
+  // FIXME: 9.0E+9 also parses back, but toString truncates the guard digit
+  // before it rounds and prints 8.0E+9 at precision 1.
+  ASSERT_EQ("8.999999E+9", convertToStringShortest(APFloat(8.999999e9f)));
+
+  // Half precision and bfloat.
+  ASSERT_EQ("0.1",
+            convertToStringShortest(APFloat(APFloat::IEEEhalf(), "0.1")));
+  ASSERT_EQ("65504",
+            convertToStringShortest(APFloat(APFloat::IEEEhalf(), "65504")));
+  // 16.0625 is exact, but 16.06 already parses back.
+  ASSERT_EQ("16.06",
+            convertToStringShortest(APFloat(APFloat::IEEEhalf(), "16.0625")));
+  ASSERT_EQ("6.0E-8", convertToStringShortest(APFloat(
+                          APFloat::IEEEhalf(), "5.9604644775390625e-8")));
+  ASSERT_EQ("0.1", convertToStringShortest(APFloat(APFloat::BFloat(), "0.1")));
+  ASSERT_EQ("3.39E+38", convertToStringShortest(APFloat(
+                            APFloat::BFloat(), "3.3895313892515355e38")));
+  // 99840 has more digits than bfloat's natural precision, so the integer
+  // rule does not apply and 1E5 is the shortest form.
+  ASSERT_EQ("1.0E+5",
+            convertToStringShortest(APFloat(APFloat::BFloat(), "99840")));
+
+  // 8-bit and smaller formats.
+  ASSERT_EQ("448",
+            convertToStringShortest(APFloat(APFloat::Float8E4M3FN(), "448")));
+  ASSERT_EQ("0.002", convertToStringShortest(
+                         APFloat(APFloat::Float8E4M3FN(), "0.001953125")));
+  // The largest Float8E5M2 has five digits; 6E4 rounds to it without
+  // overflow.
+  ASSERT_EQ("6.0E+4",
+            convertToStringShortest(APFloat(APFloat::Float8E5M2(), "57344")));
+  ASSERT_EQ("28",
+            convertToStringShortest(APFloat(APFloat::Float6E3M2FN(), "28")));
+  // "8" parses back to the largest Float6E2M3FN by saturation, not rounding.
+  ASSERT_EQ("7.5",
+            convertToStringShortest(APFloat(APFloat::Float6E2M3FN(), "7.5")));
+  ASSERT_EQ("6",
+            convertToStringShortest(APFloat(APFloat::Float4E2M1FN(), "6")));
+  ASSERT_EQ("1.5",
+            convertToStringShortest(APFloat(APFloat::Float4E2M1FN(), "1.5")));
+  ASSERT_EQ("0.5",
+            convertToStringShortest(APFloat(APFloat::Float8E8M0FNU(), "0.5")));
+  ASSERT_EQ("2.0E+38", convertToStringShortest(APFloat(
+                           APFloat::Float8E8M0FNU(), 
"1.7014118346046923e38")));
+  ASSERT_EQ("6.0E-39", convertToStringShortest(APFloat(
+                           APFloat::Float8E8M0FNU(), 
"5.877471754111438e-39")));
+  ASSERT_EQ("3.14",
+            convertToStringShortest(APFloat(APFloat::FloatTF32(), "3.14")));
+
+  // x87 extended, quad, and double-double. 1 + 2^-52 is exact in all three;
+  // the digit count follows the format's precision.
+  ASSERT_EQ("0.1", convertToStringShortest(
+                       APFloat(APFloat::x87DoubleExtended(), "0.1")));
+  ASSERT_EQ("1.0E+4000", convertToStringShortest(
+                             APFloat(APFloat::x87DoubleExtended(), "1e4000")));
+  ASSERT_EQ("1.189731495357231765E+4932",
+            convertToStringShortest(APFloat(APFloat::x87DoubleExtended(),
+                                            "1.18973149535723176502e+4932")));
+  ASSERT_EQ("4.0E-4951",
+            convertToStringShortest(APFloat(APFloat::x87DoubleExtended(),
+                                            "3.6451995318824746025e-4951")));
+  ASSERT_EQ("1.000000000000000222", convertToStringShortest(APFloat(
+                                        APFloat::x87DoubleExtended(),
+                                        "1.0000000000000002220446049250313")));
+  ASSERT_EQ("0.1",
+            convertToStringShortest(APFloat(APFloat::IEEEquad(), "0.1")));
+  ASSERT_EQ(
+      "1.189731495357231765085759326628007E+4932",
+      convertToStringShortest(APFloat(
+          APFloat::IEEEquad(), 
"1.18973149535723176508575932662800702e+4932")));
+  ASSERT_EQ("6.0E-4966", convertToStringShortest(APFloat(
+                             APFloat::IEEEquad(),
+                             "6.475175119438025110924438958227646552e-4966")));
+  ASSERT_EQ("1.0000000000000002220446049250313",
+            convertToStringShortest(APFloat(
+                APFloat::IEEEquad(), "1.0000000000000002220446049250313")));
+  ASSERT_EQ("0.1", convertToStringShortest(
+                       APFloat(APFloat::PPCDoubleDouble(), "0.1")));
+  ASSERT_EQ("3.14", convertToStringShortest(
+                        APFloat(APFloat::PPCDoubleDouble(), "3.14")));
+  ASSERT_EQ("100", convertToStringShortest(
+                       APFloat(APFloat::PPCDoubleDouble(), "100")));
+  ASSERT_EQ("1.7976931348623157E+308",
+            convertToStringShortest(
+                APFloat(APFloat::PPCDoubleDouble(), 
"1.7976931348623157e308")));
+  ASSERT_EQ("5.0E-324",
+            convertToStringShortest(APFloat(APFloat::PPCDoubleDouble(),
+                                            "4.9406564584124654e-324")));
+  ASSERT_EQ(
+      "1.0000000000000002220446049250313",
+      convertToStringShortest(APFloat(APFloat::PPCDoubleDouble(),
+                                      "1.0000000000000002220446049250313")));
+  // A negated double-double has a -0.0 low component; no decimal string
+  // parses back to it bitwise, so the natural-precision form is returned.
+  {
+    APFloat NegOne(APFloat::PPCDoubleDouble(), "1.0");
+    NegOne.changeSign();
+    ASSERT_EQ("-1", convertToStringShortest(NegOne));
+  }
+
+  // FormatMaxPadding and TruncateZero pass through to toString.
+  ASSERT_EQ("1.0E+1", convertToStringShortest(APFloat(10.0), 0));
+  ASSERT_EQ("1.0e+01", convertToStringShortest(APFloat(10.0), 0, false));
+
+  // Zero and non-finite values format exactly as toString.
+  ASSERT_EQ("0", convertToStringShortest(APFloat(0.0)));
+  ASSERT_EQ("-0", convertToStringShortest(APFloat(-0.0)));
+  ASSERT_EQ("+Inf",
+            convertToStringShortest(APFloat::getInf(APFloat::IEEEdouble())));
+  ASSERT_EQ("-Inf", convertToStringShortest(
+                        APFloat::getInf(APFloat::IEEEdouble(), true)));
+  ASSERT_EQ("NaN",
+            convertToStringShortest(APFloat::getNaN(APFloat::IEEEdouble())));
+}
+
+// Every finite value of the small formats: the string parses back bitwise
+// equal, and it is the first FormatPrecision that does so. An integer in
+// fixed notation and the largest value of a format that saturates keep the
+// natural-precision form.
+TEST(APFloatTest, toStringShortestExhaustive) {
+  const fltSemantics *Semantics[] = {
+      &APFloat::IEEEhalf(),       &APFloat::BFloat(),
+      &APFloat::Float8E5M2(),     &APFloat::Float8E5M2FNUZ(),
+      &APFloat::Float8E4M3(),     &APFloat::Float8E4M3FN(),
+      &APFloat::Float8E4M3FNUZ(), &APFloat::Float8E4M3B11FNUZ(),
+      &APFloat::Float8E3M4(),     &APFloat::Float8E8M0FNU(),
+      &APFloat::Float6E3M2FN(),   &APFloat::Float6E2M3FN(),
+      &APFloat::Float4E2M1FN(),   &APFloat::Float8E5M3FNU(),
+  };
+  for (const fltSemantics *Sem : Semantics) {
+    unsigned Bits = APFloat::getSizeInBits(*Sem);
+    for (uint64_t I = 0, E = uint64_t(1) << Bits; I != E; ++I) {
+      APFloat F(*Sem, APInt(Bits, I));
+      if (!F.isFiniteNonZero())
+        continue;
+      std::string Shortest = convertToStringShortest(F);
+      EXPECT_TRUE(APFloat(*Sem, Shortest).bitwiseIsEqual(F))
+          << Shortest << " bits 0x" << utohexstr(I);
+      SmallString<32> Str;
+      F.toString(Str);
+      bool Saturates = !APFloat::semanticsHasInf(*Sem) &&
+                       !APFloat::semanticsHasNaN(*Sem) &&
+                       abs(F).bitwiseIsEqual(APFloat::getLargest(*Sem));
+      if (Saturates || StringRef(Str).find_first_of(".Ee") == StringRef::npos) 
{
+        EXPECT_EQ(Str, Shortest) << "bits 0x" << utohexstr(I);
+        continue;
+      }
+      for (unsigned Precision = 1; Precision < 16; ++Precision) {
+        Str.clear();
+        F.toString(Str, Precision);
+        if (APFloat(*Sem, Str).bitwiseIsEqual(F)) {
+          EXPECT_EQ(Str, Shortest) << "bits 0x" << utohexstr(I);
+          break;
+        }
+      }
+    }
+  }
+}
+
 TEST(APFloatTest, toInteger) {
   bool isExact = false;
   APSInt result(5, /*isUnsigned=*/true);
diff --git a/mlir/lib/IR/AsmPrinter.cpp b/mlir/lib/IR/AsmPrinter.cpp
index a2c4c81a3ecad8..546e0a0531f2de 100644
--- a/mlir/lib/IR/AsmPrinter.cpp
+++ b/mlir/lib/IR/AsmPrinter.cpp
@@ -2298,8 +2298,10 @@ static void printFloatValue(const APFloat &apValue, 
raw_ostream &os,
   bool isNaN = apValue.isNaN();
   if (!isInf && !isNaN) {
     SmallString<128> strValue;
-    apValue.toString(strValue, /*FormatPrecision=*/6, /*FormatMaxPadding=*/0,
-                     /*TruncateZero=*/false);
+    bool isLossless = apValue.toStringRoundTrip(strValue,
+                                                /*FormatPrecision=*/6,
+                                                /*FormatMaxPadding=*/0,
+                                                /*TruncateZero=*/false);
 
     // Check to make sure that the stringized number is not some string like
     // "Inf" or NaN, that atof will accept, but the lexer will not.  Check
@@ -2309,9 +2311,8 @@ static void printFloatValue(const APFloat &apValue, 
raw_ostream &os,
              (strValue[1] >= '0' && strValue[1] <= '9'))) &&
            "[-+]?[0-9] regex does not match!");
 
-    // Parse back the stringized version and check that the value is equal
-    // (i.e., there is no precision loss).
-    if (APFloat(apValue.getSemantics(), strValue).bitwiseIsEqual(apValue)) {
+    // Lossless if it parses back to the same value.
+    if (isLossless) {
       os << strValue;
       return;
     }

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to