This is an automated email from the ASF dual-hosted git repository.

hulk pushed a commit to branch unstable
in repository https://gitbox.apache.org/repos/asf/kvrocks.git


The following commit(s) were added to refs/heads/unstable by this push:
     new bf66a9e24 chore(util): replace strtof/strtod with fast-float (#3394)
bf66a9e24 is described below

commit bf66a9e24ba3dd4b5740721d89526e3cd687f224
Author: Twice <[email protected]>
AuthorDate: Fri Mar 20 09:59:03 2026 +0800

    chore(util): replace strtof/strtod with fast-float (#3394)
    
    It closes #3344.
---
 CMakeLists.txt              |  2 ++
 NOTICE                      |  4 ++++
 cmake/fast_float.cmake      | 31 ++++++++++++++++++++++++++++
 src/common/parse_util.h     | 49 ++++++++++++++-------------------------------
 tests/cppunit/parse_util.cc | 13 ++++++++----
 5 files changed, 61 insertions(+), 38 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 11beb3a28..a6c5343dd 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -185,6 +185,7 @@ include(cmake/tbb.cmake)
 include(cmake/rocksdb.cmake)
 include(cmake/libevent.cmake)
 include(cmake/fmt.cmake)
+include(cmake/fast_float.cmake)
 include(cmake/spdlog.cmake)
 include(cmake/jsoncons.cmake)
 include(cmake/xxhash.cmake)
@@ -209,6 +210,7 @@ list(APPEND EXTERNAL_LIBS lz4)
 list(APPEND EXTERNAL_LIBS zstd)
 list(APPEND EXTERNAL_LIBS zlib_with_headers)
 list(APPEND EXTERNAL_LIBS fmt)
+list(APPEND EXTERNAL_LIBS fast_float)
 list(APPEND EXTERNAL_LIBS spdlog)
 if (ENABLE_LUAJIT)
     list(APPEND EXTERNAL_LIBS luajit)
diff --git a/NOTICE b/NOTICE
index 4ed50a5ea..dea7cdaf4 100644
--- a/NOTICE
+++ b/NOTICE
@@ -28,6 +28,10 @@ NB: RocksDB is dual-licensed under both the GPLv2 and Apache 
2.0 License.
 This product uses it under the Apache 2.0 License.
 
 * oneTBB(https://github.com/oneapi-src/oneTBB)
+* fast_float(https://github.com/fastfloat/fast_float)
+
+NB: fast_float is available under Apache-2.0, MIT, or Boost Software License 
Version 1.0.
+This product uses it under the Apache 2.0 License and reuses the text in the 
root LICENSE file.
 
 Files src/common/rocksdb_crc32c.h and src/storage/batch_debugger.h are 
modified from RocksDB.
 Files src/types/bloom_filter.* are modified from Apache Arrow.
diff --git a/cmake/fast_float.cmake b/cmake/fast_float.cmake
new file mode 100644
index 000000000..8638a4a85
--- /dev/null
+++ b/cmake/fast_float.cmake
@@ -0,0 +1,31 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+include_guard()
+
+include(cmake/utils.cmake)
+
+FetchContent_DeclareGitHubWithMirror(fast_float
+  fastfloat/fast_float v8.2.4
+  MD5=0744790f2d40c9a2e5ef021476e59796
+)
+
+FetchContent_MakeAvailableWithArgs(fast_float
+  FASTFLOAT_TEST=OFF
+  FASTFLOAT_BENCHMARKS=OFF
+  FASTFLOAT_INSTALL=OFF
+)
diff --git a/src/common/parse_util.h b/src/common/parse_util.h
index 4c5ea8507..95b1fd598 100644
--- a/src/common/parse_util.h
+++ b/src/common/parse_util.h
@@ -21,9 +21,11 @@
 #pragma once
 
 #include <charconv>
+#include <string_view>
 #include <tuple>
 #include <type_traits>
 
+#include "fast_float/fast_float.h"
 #include "status.h"
 
 template <typename T>
@@ -91,53 +93,32 @@ StatusOr<T> ParseInt(std::string_view v, NumericRange<T> 
range, int base = 10) {
 // available units: K, M, G, T, P
 StatusOr<std::uint64_t> ParseSizeAndUnit(std::string_view v);
 
-// we cannot use std::from_chars for floating-point numbers,
-// since it is available since gcc/libstdc++ 11 and libc++ 20.
-template <typename>
-struct ParseFloatFunc;
-
-template <>
-struct ParseFloatFunc<float> {
-  constexpr static const auto value = strtof;
-};
-
-template <>
-struct ParseFloatFunc<double> {
-  constexpr static const auto value = strtod;
-};
-
-template <>
-struct ParseFloatFunc<long double> {
-  constexpr static const auto value = strtold;
-};
-
 // TryParseFloat parses a string to a floating-point number,
 // it returns the first unmatched character position instead of an error status
-template <typename T = double>  // float or double
-StatusOr<ParseResultAndPos<T>> TryParseFloat(const char *str) {
-  char *end = nullptr;
+template <typename T = double, std::enable_if_t<std::is_same_v<T, float> || 
std::is_same_v<T, double>, int> = 0>
+StatusOr<ParseResultAndPos<T>> TryParseFloat(std::string_view v) {
+  T result = 0;
+  auto [end, ec] =
+      fast_float::from_chars(v.data(), v.data() + v.size(), result,
+                             fast_float::chars_format::general | 
fast_float::chars_format::allow_leading_plus);
 
-  errno = 0;
-  T result = ParseFloatFunc<T>::value(str, &end);
-
-  if (str == end) {
+  if (v.data() == end) {
     return {Status::NotOK, "not started as a number"};
   }
 
-  if (errno) {
-    return Status::FromErrno();
+  if (ec != std::errc()) {
+    return {Status::NotOK, std::make_error_code(ec).message()};
   }
 
   return {result, end};
 }
 
 // ParseFloat parses a string to a floating-point number
-template <typename T = double>  // float or double
-StatusOr<T> ParseFloat(const std::string &str) {
-  const char *begin = str.c_str();
-  auto [result, pos] = GET_OR_RET(TryParseFloat<T>(begin));
+template <typename T = double>
+StatusOr<T> ParseFloat(std::string_view str) {
+  auto [result, pos] = GET_OR_RET(TryParseFloat<T>(str));
 
-  if (pos != begin + str.size()) {
+  if (pos != str.data() + str.size()) {
     return {Status::NotOK, "encounter non-number characters"};
   }
 
diff --git a/tests/cppunit/parse_util.cc b/tests/cppunit/parse_util.cc
index 3dd00de64..ea64b9f61 100644
--- a/tests/cppunit/parse_util.cc
+++ b/tests/cppunit/parse_util.cc
@@ -21,6 +21,8 @@
 #include <gtest/gtest.h>
 #include <parse_util.h>
 
+#include <cmath>
+
 TEST(ParseUtil, TryParseInt) {
   long long v = 0;
   const char *str = "12345hellooo", *end = nullptr;
@@ -70,21 +72,24 @@ TEST(ParseUtil, ParseSizeAndUnit) {
 
 TEST(ParseUtil, ParseFloat) {
   std::string v = "1.23";
-  ASSERT_EQ(*TryParseFloat(v.c_str()), ParseResultAndPos<double>(1.23, 
v.c_str() + v.size()));
+  ASSERT_EQ(*TryParseFloat(v), ParseResultAndPos<double>(1.23, v.c_str() + 
v.size()));
 
   v = "25345.346e65hello";
-  ASSERT_EQ(*TryParseFloat(v.c_str()), ParseResultAndPos<double>(25345.346e65, 
v.c_str() + v.size() - 5));
+  ASSERT_EQ(*TryParseFloat(v), ParseResultAndPos<double>(25345.346e65, 
v.c_str() + v.size() - 5));
 
   ASSERT_FALSE(TryParseFloat("eeeeeeee"));
   ASSERT_FALSE(TryParseFloat("    "));
   ASSERT_FALSE(TryParseFloat(""));
   ASSERT_FALSE(TryParseFloat("    abcd"));
 
-  v = "   1e8   ";
-  ASSERT_EQ(*TryParseFloat(v.c_str()), ParseResultAndPos<double>(1e8, 
v.c_str() + v.size() - 3));
+  v = "1e8   ";
+  ASSERT_EQ(*TryParseFloat(v), ParseResultAndPos<double>(1e8, v.c_str() + 
v.size() - 3));
 
   ASSERT_EQ(*ParseFloat("1.23"), 1.23);
   ASSERT_EQ(*ParseFloat("1.23e2"), 1.23e2);
+  ASSERT_TRUE(std::isinf(*ParseFloat("+inf")));
+  ASSERT_TRUE(std::isnan(*ParseFloat("nan")));
   ASSERT_FALSE(ParseFloat("1.2 "));
   ASSERT_FALSE(ParseFloat("1.2hello"));
+  ASSERT_FALSE(ParseFloat("1e100000"));
 }

Reply via email to