[FXCRT] Add string<->double support routines

Add conversion routines for between strings and double precision
floating point.

Provides basis for more improved handling of text strings of numbers
due to higher precision of double vs. float.

Bug: chromium:611744
Change-Id: I917a60f081c6d13e3c55a9bd5af2301f302de373
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/60435
Commit-Queue: Lei Zhang <thestig@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
diff --git a/core/fxcrt/fx_string.cpp b/core/fxcrt/fx_string.cpp
index 590f9c8..d4a6d38 100644
--- a/core/fxcrt/fx_string.cpp
+++ b/core/fxcrt/fx_string.cpp
@@ -35,20 +35,21 @@
 
 namespace {
 
-const float fraction_scales[] = {0.1f,          0.01f,         0.001f,
-                                 0.0001f,       0.00001f,      0.000001f,
-                                 0.0000001f,    0.00000001f,   0.000000001f,
-                                 0.0000000001f, 0.00000000001f};
+constexpr float kFractionScalesFloat[] = {
+    0.1f,         0.01f,         0.001f,        0.0001f,
+    0.00001f,     0.000001f,     0.0000001f,    0.00000001f,
+    0.000000001f, 0.0000000001f, 0.00000000001f};
 
-float FractionalScale(size_t scale_factor, int value) {
-  return fraction_scales[scale_factor] * value;
-}
+const double kFractionScalesDouble[] = {
+    0.1,       0.01,       0.001,       0.0001,       0.00001,      0.000001,
+    0.0000001, 0.00000001, 0.000000001, 0.0000000001, 0.00000000001};
 
-}  // namespace
-
-float StringToFloat(ByteStringView strc) {
+template <class T>
+T StringTo(ByteStringView strc,
+           const T fractional_scales[],
+           size_t fractional_scales_size) {
   if (strc.IsEmpty())
-    return 0.0;
+    return 0;
 
   int cc = 0;
   bool bNegative = false;
@@ -64,20 +65,21 @@
       break;
     cc++;
   }
-  float value = 0;
+  T value = 0;
   while (cc < len) {
     if (strc[cc] == '.')
       break;
     value = value * 10 + FXSYS_DecimalCharToInt(strc.CharAt(cc));
     cc++;
   }
-  int scale = 0;
+  size_t scale = 0;
   if (cc < len && strc[cc] == '.') {
     cc++;
     while (cc < len) {
-      value += FractionalScale(scale, FXSYS_DecimalCharToInt(strc.CharAt(cc)));
+      value +=
+          fractional_scales[scale] * FXSYS_DecimalCharToInt(strc.CharAt(cc));
       scale++;
-      if (scale == FX_ArraySize(fraction_scales))
+      if (scale == fractional_scales_size)
         break;
       cc++;
     }
@@ -85,29 +87,26 @@
   return bNegative ? -value : value;
 }
 
-float StringToFloat(WideStringView wsStr) {
-  return StringToFloat(FX_UTF8Encode(wsStr).c_str());
-}
-
-size_t FloatToString(float f, char* buf) {
+template <class T>
+size_t ToString(T value, int (*round_func)(T), char* buf) {
   buf[0] = '0';
   buf[1] = '\0';
-  if (f == 0.0f) {
+  if (value == 0) {
     return 1;
   }
   bool bNegative = false;
-  if (f < 0) {
+  if (value < 0) {
     bNegative = true;
-    f = -f;
+    value = -value;
   }
   int scale = 1;
-  int scaled = FXSYS_roundf(f);
+  int scaled = round_func(value);
   while (scaled < 100000) {
     if (scale == 1000000) {
       break;
     }
     scale *= 10;
-    scaled = FXSYS_roundf(f * scale);
+    scaled = round_func(value * scale);
   }
   if (scaled == 0) {
     return 1;
@@ -135,3 +134,31 @@
   }
   return buf_size;
 }
+
+}  // namespace
+
+float StringToFloat(ByteStringView strc) {
+  return StringTo<float>(strc, kFractionScalesFloat,
+                         FX_ArraySize(kFractionScalesFloat));
+}
+
+float StringToFloat(WideStringView wsStr) {
+  return StringToFloat(FX_UTF8Encode(wsStr).c_str());
+}
+
+size_t FloatToString(float f, char* buf) {
+  return ToString<float>(f, FXSYS_roundf, buf);
+}
+
+double StringToDouble(ByteStringView strc) {
+  return StringTo<double>(strc, kFractionScalesDouble,
+                          FX_ArraySize(kFractionScalesDouble));
+}
+
+double StringToDouble(WideStringView wsStr) {
+  return StringToDouble(FX_UTF8Encode(wsStr).c_str());
+}
+
+size_t DoubleToString(double d, char* buf) {
+  return ToString<double>(d, FXSYS_round, buf);
+}
diff --git a/core/fxcrt/fx_string.h b/core/fxcrt/fx_string.h
index 5c048ca..4db1cea 100644
--- a/core/fxcrt/fx_string.h
+++ b/core/fxcrt/fx_string.h
@@ -25,6 +25,10 @@
 float StringToFloat(WideStringView wsStr);
 size_t FloatToString(float f, char* buf);
 
+double StringToDouble(ByteStringView str);
+double StringToDouble(WideStringView wsStr);
+size_t DoubleToString(double d, char* buf);
+
 namespace fxcrt {
 
 template <typename StrType>
diff --git a/core/fxcrt/fx_string_unittest.cpp b/core/fxcrt/fx_string_unittest.cpp
index 86b43ce..95a3821 100644
--- a/core/fxcrt/fx_string_unittest.cpp
+++ b/core/fxcrt/fx_string_unittest.cpp
@@ -2,9 +2,23 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <limits>
+
 #include "core/fxcrt/fx_string.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+char* TerminatedFloatToString(float value, char* buf) {
+  size_t buflen = FloatToString(value, buf);
+  buf[buflen] = '\0';
+  return buf;
+}
+
+char* TerminatedDoubleToString(double value, char* buf) {
+  size_t buflen = DoubleToString(value, buf);
+  buf[buflen] = '\0';
+  return buf;
+}
+
 TEST(fxstring, FX_UTF8Encode) {
   EXPECT_EQ("", FX_UTF8Encode(WideStringView()));
   EXPECT_EQ(
@@ -77,6 +91,105 @@
   EXPECT_FLOAT_EQ(1.999999881f, StringToFloat("1.999999881"));
 }
 
+TEST(fxstring, FloatToString) {
+  char buf[32];
+
+  EXPECT_STREQ("0", TerminatedFloatToString(0.0f, buf));
+  EXPECT_STREQ("0", TerminatedFloatToString(-0.0f, buf));
+  EXPECT_STREQ("0",
+               TerminatedFloatToString(std::numeric_limits<float>::min(), buf));
+  EXPECT_STREQ(
+      "0", TerminatedFloatToString(-std::numeric_limits<float>::min(), buf));
+
+  EXPECT_STREQ("0.25", TerminatedFloatToString(0.25f, buf));
+  EXPECT_STREQ("-0.25", TerminatedFloatToString(-0.25f, buf));
+
+  EXPECT_STREQ("100", TerminatedFloatToString(100.0f, buf));
+  EXPECT_STREQ("-100", TerminatedFloatToString(-100.0f, buf));
+
+  // FloatToString won't convert beyond the maximum integer, and values
+  // larger than that get converted to a string representing that.
+  EXPECT_STREQ("2147483647", TerminatedFloatToString(2147483647.0f, buf));
+  EXPECT_STREQ("2147483647", TerminatedFloatToString(2147483647.5f, buf));
+  EXPECT_STREQ("2147483647",
+               TerminatedFloatToString(std::numeric_limits<float>::max(), buf));
+
+  // FloatToString won't convert beyond the minimum integer, and values
+  // smaller than that get converted to a string representing that.
+  EXPECT_STREQ("-2147483647", TerminatedFloatToString(-2147483647.0f, buf));
+  EXPECT_STREQ("-2147483647", TerminatedFloatToString(-2147483647.5f, buf));
+  EXPECT_STREQ("-2147483647", TerminatedFloatToString(
+                                  -std::numeric_limits<float>::max(), buf));
+
+  // Conversion only acknowledges precision to 5 digit past decimal, and
+  // rounds beyond that.
+  EXPECT_STREQ("1", TerminatedFloatToString(1.000001119f, buf));
+  EXPECT_STREQ("1.00001", TerminatedFloatToString(1.000011119f, buf));
+  EXPECT_STREQ("1.99999", TerminatedFloatToString(1.999988881f, buf));
+  EXPECT_STREQ("2", TerminatedFloatToString(1.999999881f, buf));
+}
+
+TEST(fxstring, StringToDouble) {
+  EXPECT_FLOAT_EQ(0.0, StringToDouble(""));
+  EXPECT_FLOAT_EQ(0.0, StringToDouble("0"));
+  EXPECT_FLOAT_EQ(0.0, StringToDouble("0.0"));
+  EXPECT_FLOAT_EQ(0.0, StringToDouble("-0.0"));
+
+  EXPECT_FLOAT_EQ(0.25, StringToDouble("0.25"));
+  EXPECT_FLOAT_EQ(-0.25, StringToDouble("-0.25"));
+
+  EXPECT_FLOAT_EQ(100.0, StringToDouble("100"));
+  EXPECT_FLOAT_EQ(100.0, StringToDouble("100.0"));
+  EXPECT_FLOAT_EQ(100.0, StringToDouble("    100.0"));
+  EXPECT_FLOAT_EQ(-100.0, StringToDouble("-100.0000"));
+
+  EXPECT_FLOAT_EQ(3.402823e+38,
+                  StringToDouble("340282300000000000000000000000000000000"));
+  EXPECT_FLOAT_EQ(-3.402823e+38,
+                  StringToDouble("-340282300000000000000000000000000000000"));
+
+  EXPECT_FLOAT_EQ(1.000000119, StringToDouble("1.000000119"));
+  EXPECT_FLOAT_EQ(1.999999881, StringToDouble("1.999999881"));
+}
+
+TEST(fxstring, DoubleToString) {
+  char buf[32];
+
+  EXPECT_STREQ("0", TerminatedDoubleToString(0.0f, buf));
+  EXPECT_STREQ("0", TerminatedDoubleToString(-0.0f, buf));
+  EXPECT_STREQ(
+      "0", TerminatedDoubleToString(std::numeric_limits<double>::min(), buf));
+  EXPECT_STREQ(
+      "0", TerminatedDoubleToString(-std::numeric_limits<double>::min(), buf));
+
+  EXPECT_STREQ("0.25", TerminatedDoubleToString(0.25f, buf));
+  EXPECT_STREQ("-0.25", TerminatedDoubleToString(-0.25f, buf));
+
+  EXPECT_STREQ("100", TerminatedDoubleToString(100.0f, buf));
+  EXPECT_STREQ("-100", TerminatedDoubleToString(-100.0f, buf));
+
+  // DoubleToString won't convert beyond the maximum integer, and values
+  // larger than that get converted to a string representing that.
+  EXPECT_STREQ("2147483647", TerminatedDoubleToString(2147483647.0f, buf));
+  EXPECT_STREQ("2147483647", TerminatedDoubleToString(2147483647.5f, buf));
+  EXPECT_STREQ("2147483647", TerminatedDoubleToString(
+                                 std::numeric_limits<double>::max(), buf));
+
+  // DoubleToString won't convert beyond the minimum integer, and values
+  // smaller than that get converted to a string representing that.
+  EXPECT_STREQ("-2147483647", TerminatedDoubleToString(-2147483647.0f, buf));
+  EXPECT_STREQ("-2147483647", TerminatedDoubleToString(-2147483647.5f, buf));
+  EXPECT_STREQ("-2147483647", TerminatedDoubleToString(
+                                  -std::numeric_limits<double>::max(), buf));
+
+  // Conversion only acknowledges precision to 5 digit past decimal, and
+  // rounds beyond that.
+  EXPECT_STREQ("1", TerminatedDoubleToString(1.000001119f, buf));
+  EXPECT_STREQ("1.00001", TerminatedDoubleToString(1.000011119f, buf));
+  EXPECT_STREQ("1.99999", TerminatedDoubleToString(1.999988881f, buf));
+  EXPECT_STREQ("2", TerminatedDoubleToString(1.999999881f, buf));
+}
+
 TEST(fxstring, SplitByteString) {
   std::vector<ByteString> result;
   result = fxcrt::Split(ByteString(""), ',');