Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package fast_float for openSUSE:Factory 
checked in at 2026-11-03 17:53:16
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/fast_float (Old)
 and      /work/SRC/openSUSE:Factory/.fast_float.new.1257 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "fast_float"

Tue Nov  3 17:53:16 2026 rev:15 rq:1394425 version:8.3.3

Changes:
--------
--- /work/SRC/openSUSE:Factory/fast_float/fast_float.changes    2026-10-11 
15:14:45.199302198 +0200
+++ /work/SRC/openSUSE:Factory/.fast_float.new.1257/fast_float.changes  
2026-11-03 17:54:05.072606423 +0100
@@ -1,0 +2,9 @@
+Mon Nov  2 10:04:53 UTC 2026 - Jan Engelhardt <[email protected]>
+
+- Update to release 8.3.3
+  * std::float32_t/std::float64_t is left unmodified on
+    invalid input
+  * Right-shifting a negative value in detail::power() is avoided
+  * integer_times_pow10 arguments wider than 64 bits are rejected
+
+-------------------------------------------------------------------

Old:
----
  v8.3.2.tar.gz

New:
----
  v8.3.3.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ fast_float.spec ++++++
--- /var/tmp/diff_new_pack.aH2WuP/_old  2026-11-03 17:54:06.611670750 +0100
+++ /var/tmp/diff_new_pack.aH2WuP/_new  2026-11-03 17:54:06.614670875 +0100
@@ -17,7 +17,7 @@
 
 
 Name:           fast_float
-Version:        8.3.2
+Version:        8.3.3
 Release:        0
 Summary:        Re-implementation of std::from_chars for parsing strings into 
numbers
 License:        Apache-2.0 OR BSL-1.0 OR MIT

++++++ _scmsync.obsinfo ++++++
--- /var/tmp/diff_new_pack.aH2WuP/_old  2026-11-03 17:54:06.686673885 +0100
+++ /var/tmp/diff_new_pack.aH2WuP/_new  2026-11-03 17:54:06.694674219 +0100
@@ -1,5 +1,5 @@
-mtime: 1791582352
-commit: 5847ec360eba64490bdac4c03626b0ec0a3768a5dbe797b6936c93f305ad4f60
+mtime: 1793614918
+commit: beac1185734ccb28d1839c2a85e5db31d9621aa6064bb3667a11c13ba3e07693
 url: https://src.opensuse.org/jengelh/fast_float
 revision: master
 

++++++ build.specials.obscpio ++++++

++++++ build.specials.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/.gitignore new/.gitignore
--- old/.gitignore      1970-01-01 01:00:00.000000000 +0100
+++ new/.gitignore      2026-11-02 11:21:58.000000000 +0100
@@ -0,0 +1 @@
+.osc

++++++ v8.3.2.tar.gz -> v8.3.3.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/fast_float-8.3.2/.github/workflows/ubuntu22-clang.yml 
new/fast_float-8.3.3/.github/workflows/ubuntu22-clang.yml
--- old/fast_float-8.3.2/.github/workflows/ubuntu22-clang.yml   2026-10-09 
21:56:38.000000000 +0200
+++ new/fast_float-8.3.3/.github/workflows/ubuntu22-clang.yml   2026-10-31 
14:45:33.000000000 +0100
@@ -9,6 +9,14 @@
       - uses: actions/checkout@v7
       - name: Install clang++-14
         run: sudo apt-get install -y clang++-14
+      - name: Check for pedantic warnings in pre-C++20 modes (issue 427)
+        run: |
+          set -xe
+          printf '#include "fast_float/fast_float.h"\nint main() { return 0; 
}\n' > pedantic_check.cpp
+          for std in c++11 c++14 c++17 c++20; do
+            clang++-14 -std=$std -Wall -Wextra -pedantic-errors -Werror \
+              -Iinclude -fsyntax-only pedantic_check.cpp
+          done
       - name: Use cmake
         run: |
           mkdir build &&
@@ -22,4 +30,4 @@
           cd build20 &&
           CXX=clang++-14 cmake -DFASTFLOAT_CONSTEXPR_TESTS=ON 
-DFASTFLOAT_CXX_STANDARD=20 -DFASTFLOAT_TEST=ON  ..  &&
           cmake --build .   &&
-          ctest --output-on-failure
\ No newline at end of file
+          ctest --output-on-failure
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fast_float-8.3.2/CMakeLists.txt 
new/fast_float-8.3.3/CMakeLists.txt
--- old/fast_float-8.3.2/CMakeLists.txt 2026-10-09 21:56:38.000000000 +0200
+++ new/fast_float-8.3.3/CMakeLists.txt 2026-10-31 14:45:33.000000000 +0100
@@ -1,7 +1,7 @@
 cmake_minimum_required(VERSION 3.14)
 
 
-project(fast_float VERSION 8.3.2 LANGUAGES CXX)
+project(fast_float VERSION 8.3.3 LANGUAGES CXX)
 set(FASTFLOAT_CXX_STANDARD 11 CACHE STRING "the C++ standard to use for 
fastfloat")
 set(CMAKE_CXX_STANDARD ${FASTFLOAT_CXX_STANDARD})
 option(FASTFLOAT_TEST "Enable tests" OFF)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fast_float-8.3.2/MODULE.bazel 
new/fast_float-8.3.3/MODULE.bazel
--- old/fast_float-8.3.2/MODULE.bazel   2026-10-09 21:56:38.000000000 +0200
+++ new/fast_float-8.3.3/MODULE.bazel   2026-10-31 14:45:33.000000000 +0100
@@ -2,7 +2,7 @@
 
 module(
     name = "fast_float",
-    version = "8.3.2",
+    version = "8.3.3",
     compatibility_level = 8,
 )
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fast_float-8.3.2/README.md 
new/fast_float-8.3.3/README.md
--- old/fast_float-8.3.2/README.md      2026-10-09 21:56:38.000000000 +0200
+++ new/fast_float-8.3.3/README.md      2026-10-31 14:45:33.000000000 +0100
@@ -482,7 +482,7 @@
 ```
 
 Overloads of `fast_float::integer_times_pow10()` are provided for
-signed and unsigned integer types: `int64_t`, `uint64_t`, etc.
+signed and unsigned integer types up to 64 bits: `int64_t`, `uint64_t`, etc.
 
 
 ## Users and Related Work
@@ -587,7 +587,7 @@
 FetchContent_Declare(
   fast_float
   GIT_REPOSITORY https://github.com/fastfloat/fast_float.git
-  GIT_TAG tags/v8.3.2
+  GIT_TAG tags/v8.3.3
   GIT_SHALLOW TRUE)
 
 FetchContent_MakeAvailable(fast_float)
@@ -603,7 +603,7 @@
 CPMAddPackage(
   NAME fast_float
   GITHUB_REPOSITORY "fastfloat/fast_float"
-  GIT_TAG v8.3.2)
+  GIT_TAG v8.3.3)
 ```
 
 ## Using as single header
@@ -615,7 +615,7 @@
 
 You may directly download automatically generated single-header files:
 
-<https://github.com/fastfloat/fast_float/releases/download/v8.3.2/fast_float.h>
+<https://github.com/fastfloat/fast_float/releases/download/v8.3.3/fast_float.h>
 
 ## Benchmarking
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fast_float-8.3.2/fuzz/from_chars.cc 
new/fast_float-8.3.3/fuzz/from_chars.cc
--- old/fast_float-8.3.2/fuzz/from_chars.cc     2026-10-09 21:56:38.000000000 
+0200
+++ new/fast_float-8.3.3/fuzz/from_chars.cc     2026-10-31 14:45:33.000000000 
+0100
@@ -1,38 +1,205 @@
 #include "fast_float/fast_float.h"
 #include <fuzzer/FuzzedDataProvider.h>
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
 #include <string>
 #include <system_error>
+#include <type_traits>
+#include <vector>
+
+#if defined(__has_include)
+#if __has_include(<charconv>) && __cplusplus >= 201703L
+#include <charconv>
+#define FASTFLOAT_FUZZ_CHARCONV 1
+#endif
+#endif
+
+static void fail(char const *check) {
+  fprintf(stderr, "fast_float fuzz failure: %s\n", check);
+  abort();
+}
 
 fast_float::chars_format arbitrary_format(FuzzedDataProvider &fdp) {
   using fast_float::chars_format;
-  switch (fdp.ConsumeIntegralInRange<int>(0, 3)) {
+  chars_format format;
+  switch (fdp.ConsumeIntegralInRange<int>(0, 7)) {
   case 0:
-    return chars_format::scientific;
+    format = chars_format::scientific;
     break;
   case 1:
-    return chars_format::fixed;
+    format = chars_format::fixed;
     break;
   case 2:
-    return chars_format::fixed;
+    format = chars_format::json;
+    break;
+  case 3:
+    format = chars_format::json_or_infnan;
+    break;
+  case 4:
+    format = chars_format::fortran;
     break;
+  case 5:
+    format = chars_format::javascript;
+    break;
+  case 6:
+    format = chars_format::javascript_sloppy;
+    break;
+  default:
+    format = chars_format::general;
+    break;
+  }
+  if (fdp.ConsumeBool()) {
+    format = format | chars_format::allow_leading_plus;
+  }
+  if (fdp.ConsumeBool()) {
+    format = format | chars_format::skip_white_space;
+  }
+  return format;
+}
+
+template <typename T> uint64_t bits_of(T value) {
+  uint64_t bits = 0;
+  memcpy(&bits, &value, sizeof(T));
+  return bits;
+}
+
+// The same ASCII text must parse identically for every character type: same
+// error code, same number of consumed code units, bit-identical value.
+template <typename UC>
+void check_widened(std::string const &input,
+                   fast_float::from_chars_result_t<char> expected,
+                   double expected_value, fast_float::chars_format format) {
+  std::vector<UC> wide;
+  wide.reserve(input.size());
+  for (char c : input) {
+    wide.push_back(UC(static_cast<unsigned char>(c)));
+  }
+  double value = 0.0;
+  auto result = fast_float::from_chars(wide.data(), wide.data() + wide.size(),
+                                       value, format);
+  if (result.ec != expected.ec ||
+      (result.ptr - wide.data()) != (expected.ptr - input.data())) {
+    fail("wide result disagrees with char result");
+  }
+  if (expected.ec == std::errc() && bits_of(value) != bits_of(expected_value)) 
{
+    fail("wide value disagrees with char value");
+  }
+}
+
+// Every number format accepted by the library is pure ASCII, so a successful
+// parse must never consume a code unit >= 0x80. A violation means some helper
+// inspected only the low byte of a wide code unit (the bug behind #391).
+// The generated sequences keep an ASCII low byte but take hostile high bits:
+// top-bit-set units (near the minimum of a signed wchar_t, the shape behind
+// #422), sign-extended negative units (the shape behind #395), and non-ASCII
+// code points whose low byte is an ASCII digit (the shape behind #391).
+template <typename UC>
+void check_arbitrary_units(FuzzedDataProvider &fdp,
+                           fast_float::chars_format format) {
+  using unsigned_uc = typename std::make_unsigned<UC>::type;
+  size_t count = fdp.ConsumeIntegralInRange<size_t>(0, 64);
+  std::vector<UC> units;
+  units.reserve(count);
+  for (size_t i = 0; i < count; ++i) {
+    unsigned_uc unit = fdp.ConsumeIntegral<uint8_t>();
+    switch (fdp.ConsumeIntegralInRange<int>(0, 4)) {
+    case 0: // top bit set: near the minimum value when UC is signed
+      unit = unsigned_uc(unit | (unsigned_uc(1) << (sizeof(UC) * 8 - 1)));
+      break;
+    case 1: // all high bits set: a small negative value when UC is signed
+      unit = unsigned_uc(unit | ~unsigned_uc(0xFF));
+      break;
+    case 2: // arbitrary high byte: a non-ASCII code point, ASCII low byte
+      unit = unsigned_uc(
+          unit | unsigned_uc(unsigned_uc(fdp.ConsumeIntegral<uint8_t>()) << 
8));
+      break;
+    default: // plain ASCII unit
+      break;
+    }
+    units.push_back(UC(unit));
+  }
+  if (units.empty()) {
+    return;
+  }
+  UC const *begin = units.data();
+  UC const *end = begin + units.size();
+
+  double as_double = 0.0;
+  auto result = fast_float::from_chars(begin, end, as_double, format);
+  if (result.ec == std::errc()) {
+    for (UC const *p = begin; p != result.ptr; ++p) {
+      if (static_cast<unsigned_uc>(*p) >= 0x80) {
+        fail("float parse consumed a non-ASCII code unit");
+      }
+    }
+  }
+  uint16_t as_uint16 = 0;
+  auto int_result = fast_float::from_chars(begin, end, as_uint16);
+  if (int_result.ec == std::errc()) {
+    for (UC const *p = begin; p != int_result.ptr; ++p) {
+      if (static_cast<unsigned_uc>(*p) >= 0x80) {
+        fail("integer parse consumed a non-ASCII code unit");
+      }
+    }
+  }
+}
+
+// fast_float's integer parsing is specified to match std::from_chars.
+template <typename T> void check_int(std::string const &input, int base) {
+  T value{};
+  auto result = fast_float::from_chars(
+      input.data(), input.data() + input.size(), value, base);
+#ifdef FASTFLOAT_FUZZ_CHARCONV
+  T expected{};
+  auto std_result = std::from_chars(input.data(), input.data() + input.size(),
+                                    expected, base);
+  if (result.ec != std_result.ec || result.ptr != std_result.ptr) {
+    fail("integer result disagrees with std::from_chars");
   }
-  return chars_format::general;
+  if (result.ec == std::errc() && value != expected) {
+    fail("integer value disagrees with std::from_chars");
+  }
+#else
+  static_cast<void>(result);
+#endif
+}
+
+template <typename T>
+void check_int_all_bases(std::string const &input, int base) {
+  check_int<T>(input, 10);
+  check_int<T>(input, base);
 }
 
 extern "C" int LLVMFuzzerTestOneInput(uint8_t const *data, size_t size) {
   FuzzedDataProvider fdp(data, size);
   fast_float::chars_format format = arbitrary_format(fdp);
-  double result_d = 0.0;
+  int base = fdp.ConsumeIntegralInRange<int>(2, 36);
+
   std::string input_d = fdp.ConsumeRandomLengthString(128);
-  auto answer = fast_float::from_chars(
+  double result_d = 0.0;
+  auto answer_d = fast_float::from_chars(
       input_d.data(), input_d.data() + input_d.size(), result_d, format);
-  std::string input_f = fdp.ConsumeRandomLengthString(128);
   float result_f = 0.0;
-  answer = fast_float::from_chars(
-      input_f.data(), input_f.data() + input_f.size(), result_f, format);
-  int result_i = 0;
+  fast_float::from_chars(input_d.data(), input_d.data() + input_d.size(),
+                         result_f, format);
+  check_widened<wchar_t>(input_d, answer_d, result_d, format);
+  check_widened<char16_t>(input_d, answer_d, result_d, format);
+  check_widened<char32_t>(input_d, answer_d, result_d, format);
+
   std::string input_i = fdp.ConsumeRandomLengthString(128);
-  answer = fast_float::from_chars(input_i.data(),
-                                  input_i.data() + input_i.size(), result_i);
+  check_int_all_bases<uint8_t>(input_i, base);
+  check_int_all_bases<int8_t>(input_i, base);
+  check_int_all_bases<uint16_t>(input_i, base);
+  check_int_all_bases<int16_t>(input_i, base);
+  check_int_all_bases<uint32_t>(input_i, base);
+  check_int_all_bases<int32_t>(input_i, base);
+  check_int_all_bases<uint64_t>(input_i, base);
+  check_int_all_bases<int64_t>(input_i, base);
+
+  check_arbitrary_units<wchar_t>(fdp, format);
+  check_arbitrary_units<char16_t>(fdp, format);
+  check_arbitrary_units<char32_t>(fdp, format);
   return 0;
-}
\ No newline at end of file
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fast_float-8.3.2/include/fast_float/ascii_number.h 
new/fast_float-8.3.3/include/fast_float/ascii_number.h
--- old/fast_float-8.3.2/include/fast_float/ascii_number.h      2026-10-09 
21:56:38.000000000 +0200
+++ new/fast_float-8.3.3/include/fast_float/ascii_number.h      2026-10-31 
14:45:33.000000000 +0100
@@ -622,10 +622,22 @@
   // JSON and JavaScript are mutually exclusive, so only the non-JSON
   // instantiation has to look at the flag.
   FASTFLOAT_IF_CONSTEXPR17(!basic_json_fmt) {
+    // We have to disable -Wc++20-extensions for the [[unlikely]] attribute
+    // See comment for @jwakely at
+    // https://github.com/fastfloat/fast_float/pull/387#discussion_r3366943539
+#ifdef __clang__
+#pragma clang diagnostic push
+#if (!defined(__APPLE_CC__) && __clang_major__ >= 10) || (__clang_major__ >= 
13)
+#pragma clang diagnostic ignored "-Wc++20-extensions"
+#endif
+#endif
     if fastfloat_unlikely (uint64_t(options.format &
                                     detail::basic_javascript_fmt)) {
       return parse_number_string_javascript<UC>(p, pend, options, store_spans);
     }
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
   }
   return parse_number_string_impl<basic_json_fmt, false, UC>(p, pend, options,
                                                              store_spans);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/fast_float-8.3.2/include/fast_float/decimal_to_binary.h 
new/fast_float-8.3.3/include/fast_float/decimal_to_binary.h
--- old/fast_float-8.3.2/include/fast_float/decimal_to_binary.h 2026-10-09 
21:56:38.000000000 +0200
+++ new/fast_float-8.3.3/include/fast_float/decimal_to_binary.h 2026-10-31 
14:45:33.000000000 +0100
@@ -61,9 +61,15 @@
  *   -ceil(p) + q
  * where
  *   p = log(5**-q)/log(2) = -q * log(5)/log(2)
+ *
+ * The shift must floor towards negative infinity, as an arithmetic right
+ * shift does, but right-shifting a negative value is implementation-defined
+ * before C++20. Biasing the product by 2048 * 65536 keeps the shifted value
+ * non-negative without changing the result: the bias is a multiple of 2^16
+ * and is removed again after the shift.
  */
 constexpr fastfloat_really_inline int32_t power(int32_t q) noexcept {
-  return (((152170 + 65536) * q) >> 16) + 63;
+  return (((152170 + 65536) * q + (2048 * 65536)) >> 16) - 2048 + 63;
 }
 } // namespace detail
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fast_float-8.3.2/include/fast_float/float_common.h 
new/fast_float-8.3.3/include/fast_float/float_common.h
--- old/fast_float-8.3.2/include/fast_float/float_common.h      2026-10-09 
21:56:38.000000000 +0200
+++ new/fast_float-8.3.3/include/fast_float/float_common.h      2026-10-31 
14:45:33.000000000 +0100
@@ -18,7 +18,7 @@
 
 #define FASTFLOAT_VERSION_MAJOR 8
 #define FASTFLOAT_VERSION_MINOR 3
-#define FASTFLOAT_VERSION_PATCH 2
+#define FASTFLOAT_VERSION_PATCH 3
 
 #define FASTFLOAT_STRINGIZE_IMPL(x) #x
 #define FASTFLOAT_STRINGIZE(x) FASTFLOAT_STRINGIZE_IMPL(x)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fast_float-8.3.2/include/fast_float/parse_number.h 
new/fast_float-8.3.3/include/fast_float/parse_number.h
--- old/fast_float-8.3.2/include/fast_float/parse_number.h      2026-10-09 
21:56:38.000000000 +0200
+++ new/fast_float-8.3.3/include/fast_float/parse_number.h      2026-10-31 
14:45:33.000000000 +0100
@@ -158,7 +158,10 @@
     // float32_t
     float val = 0.0f;
     auto ret = from_chars_advanced(first, last, val, options);
-    value = val;
+    // If no characters match the pattern, value must be left unmodified.
+    if (ret.ec != std::errc::invalid_argument) {
+      value = val;
+    }
     return ret;
   }
 };
@@ -175,7 +178,10 @@
     // float64_t
     double val = 0.0;
     auto ret = from_chars_advanced(first, last, val, options);
-    value = val;
+    // If no characters match the pattern, value must be left unmodified.
+    if (ret.ec != std::errc::invalid_argument) {
+      value = val;
+    }
     return ret;
   }
 };
@@ -385,9 +391,19 @@
   // Leave for the javascript conversion before anything else is live, so this
   // compiles to a tail call and the common path keeps its registers. The cold
   // function repeats the leading-whitespace and empty-input handling below.
+  // See the comment on -Wc++20-extensions further down in this function.
+#ifdef __clang__
+#pragma clang diagnostic push
+#if (!defined(__APPLE_CC__) && __clang_major__ >= 10) || (__clang_major__ >= 
13)
+#pragma clang diagnostic ignored "-Wc++20-extensions"
+#endif
+#endif
   if fastfloat_unlikely (uint64_t(fmt & detail::basic_javascript_fmt)) {
     return from_chars_float_javascript<T, UC>(first, last, value, options);
   }
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
 
   from_chars_result_t<UC> answer;
   if (uint64_t(fmt & chars_format::skip_white_space)) {
@@ -529,38 +545,42 @@
 // the following overloads are here to avoid surprising ambiguity for int,
 // unsigned, etc.
 template <typename T, typename Int>
-FASTFLOAT_CONSTEXPR20
-    typename std::enable_if<is_supported_float_type<T>::value &&
-                                std::is_integral<Int>::value &&
-                                !std::is_signed<Int>::value,
-                            T>::type
-    integer_times_pow10(Int mantissa, int decimal_exponent) noexcept {
+FASTFLOAT_CONSTEXPR20 typename std::enable_if<
+    is_supported_float_type<T>::value && std::is_integral<Int>::value &&
+        sizeof(Int) <= sizeof(uint64_t) && !std::is_signed<Int>::value,
+    T>::type
+integer_times_pow10(Int mantissa, int decimal_exponent) noexcept {
   return integer_times_pow10<T>(static_cast<uint64_t>(mantissa),
                                 decimal_exponent);
 }
 
 template <typename T, typename Int>
-FASTFLOAT_CONSTEXPR20
-    typename std::enable_if<is_supported_float_type<T>::value &&
-                                std::is_integral<Int>::value &&
-                                std::is_signed<Int>::value,
-                            T>::type
-    integer_times_pow10(Int mantissa, int decimal_exponent) noexcept {
+FASTFLOAT_CONSTEXPR20 typename std::enable_if<
+    is_supported_float_type<T>::value && std::is_integral<Int>::value &&
+        sizeof(Int) <= sizeof(int64_t) && std::is_signed<Int>::value,
+    T>::type
+integer_times_pow10(Int mantissa, int decimal_exponent) noexcept {
   return integer_times_pow10<T>(static_cast<int64_t>(mantissa),
                                 decimal_exponent);
 }
 
 template <typename Int>
-FASTFLOAT_CONSTEXPR20 typename std::enable_if<
-    std::is_integral<Int>::value && !std::is_signed<Int>::value, double>::type
-integer_times_pow10(Int mantissa, int decimal_exponent) noexcept {
+FASTFLOAT_CONSTEXPR20
+    typename std::enable_if<std::is_integral<Int>::value &&
+                                sizeof(Int) <= sizeof(uint64_t) &&
+                                !std::is_signed<Int>::value,
+                            double>::type
+    integer_times_pow10(Int mantissa, int decimal_exponent) noexcept {
   return integer_times_pow10(static_cast<uint64_t>(mantissa), 
decimal_exponent);
 }
 
 template <typename Int>
-FASTFLOAT_CONSTEXPR20 typename std::enable_if<
-    std::is_integral<Int>::value && std::is_signed<Int>::value, double>::type
-integer_times_pow10(Int mantissa, int decimal_exponent) noexcept {
+FASTFLOAT_CONSTEXPR20
+    typename std::enable_if<std::is_integral<Int>::value &&
+                                sizeof(Int) <= sizeof(int64_t) &&
+                                std::is_signed<Int>::value,
+                            double>::type
+    integer_times_pow10(Int mantissa, int decimal_exponent) noexcept {
   return integer_times_pow10(static_cast<int64_t>(mantissa), decimal_exponent);
 }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fast_float-8.3.2/tests/basictest.cpp 
new/fast_float-8.3.3/tests/basictest.cpp
--- old/fast_float-8.3.2/tests/basictest.cpp    2026-10-09 21:56:38.000000000 
+0200
+++ new/fast_float-8.3.3/tests/basictest.cpp    2026-10-31 14:45:33.000000000 
+0100
@@ -2283,6 +2283,38 @@
 }
 } // namespace all_supported_types
 
+#if defined(__SIZEOF_INT128__)
+template <typename Int, typename = void>
+struct has_integer_times_pow10 : std::false_type {};
+
+template <typename Int>
+struct has_integer_times_pow10<
+    Int, std::void_t<decltype(fast_float::integer_times_pow10(Int{}, 0))>>
+    : std::true_type {};
+
+template <typename Float, typename Int, typename = void>
+struct has_typed_integer_times_pow10 : std::false_type {};
+
+template <typename Float, typename Int>
+struct has_typed_integer_times_pow10<
+    Float, Int,
+    std::void_t<decltype(fast_float::integer_times_pow10<Float>(Int{}, 0))>>
+    : std::true_type {};
+
+TEST_CASE("integer_times_pow10 rejects integers wider than 64 bits") {
+  CHECK(has_integer_times_pow10<int64_t>::value);
+  CHECK(has_integer_times_pow10<uint64_t>::value);
+  CHECK(has_typed_integer_times_pow10<float, int64_t>::value);
+  CHECK(has_typed_integer_times_pow10<double, uint64_t>::value);
+  CHECK_FALSE(has_integer_times_pow10<__int128_t>::value);
+  CHECK_FALSE(has_integer_times_pow10<__uint128_t>::value);
+  CHECK_FALSE(has_typed_integer_times_pow10<float, __int128_t>::value);
+  CHECK_FALSE(has_typed_integer_times_pow10<float, __uint128_t>::value);
+  CHECK_FALSE(has_typed_integer_times_pow10<double, __int128_t>::value);
+  CHECK_FALSE(has_typed_integer_times_pow10<double, __uint128_t>::value);
+}
+#endif
+
 TEST_CASE("integer_times_pow10") {
   /* explicitly verifying API with different types of integers */
   // double (the "default" overload)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fast_float-8.3.2/tests/fixedwidthtest.cpp 
new/fast_float-8.3.3/tests/fixedwidthtest.cpp
--- old/fast_float-8.3.2/tests/fixedwidthtest.cpp       2026-10-09 
21:56:38.000000000 +0200
+++ new/fast_float-8.3.3/tests/fixedwidthtest.cpp       2026-10-31 
14:45:33.000000000 +0100
@@ -2,6 +2,8 @@
 #include <iostream>
 #include <vector>
 #include <cstring>
+#include <limits>
+#include <string_view>
 #include "fast_float/fast_float.h"
 #include <cstdint>
 
@@ -9,6 +11,39 @@
 #include <stdfloat>
 #endif
 
+// If no characters match the pattern, value must be left unmodified
+// (see https://github.com/fastfloat/fast_float/issues/424).
+template <typename T> bool check_invalid_input_preserves_value() {
+  std::vector<std::string_view> const invalid_inputs{"x",  "",   "-",  ".",
+                                                     "e1", "-x", "na", "+"};
+  for (auto const &f : invalid_inputs) {
+    T result = T(42);
+    auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), 
result);
+    if (answer.ec != std::errc::invalid_argument || answer.ptr != f.data()) {
+      std::cerr << "Expected invalid_argument for input: \"" << f << "\""
+                << std::endl;
+      return false;
+    }
+    if (result != T(42)) {
+      std::cerr << "Value modified on invalid input: \"" << f << "\" got "
+                << result << std::endl;
+      return false;
+    }
+  }
+  // Out-of-range input must still store the documented value.
+  std::string_view const too_large = "1e999";
+  T result = T(42);
+  auto answer = fast_float::from_chars(
+      too_large.data(), too_large.data() + too_large.size(), result);
+  if (answer.ec != std::errc::result_out_of_range ||
+      result != std::numeric_limits<T>::infinity()) {
+    std::cerr << "Expected infinity and result_out_of_range for \"1e999\""
+              << std::endl;
+    return false;
+  }
+  return true;
+}
+
 int main() {
   // Write some testcases for the parsing of floating point numbers in the
   // float32_t type. We use the from_chars function defined in this library.
@@ -33,6 +68,9 @@
       return EXIT_FAILURE;
     }
   }
+  if (!check_invalid_input_preserves_value<std::float32_t>()) {
+    return EXIT_FAILURE;
+  }
 #else
   std::cout << "No std::float32_t type available." << std::endl;
 #endif
@@ -60,6 +98,9 @@
       return EXIT_FAILURE;
     }
   }
+  if (!check_invalid_input_preserves_value<std::float64_t>()) {
+    return EXIT_FAILURE;
+  }
 #else
   std::cout << "No std::float64_t type available." << std::endl;
 #endif

Reply via email to