[FXCRT] Add double rounding support

Double precision rounding support is useful for improved accuracy by
the automatic conversion of decimal text entry (e.g., currency).

in crbug.com/611744 the Javascript parsing code converts a string from a
textbox into a single precision float.  That has enough floating point
error that input like "407.96" gets changed into "407.959991".

As noted in https://crbug.com/611744#c3, there is no code to dictate
forwarding, so any floating point error will show up.

Almost all the math in the Javascript code uses double except for that
one initial parse, which is float.  Once this CL is in then it will
facilitate using double for that too, and then "407.96" will still show
up as "407.96".

There are of course other input cases where numerical inaccuracies could
still arise, but at least with this it will ensure that Pdfium will behave
like other readers.

Bug: chromium:611744
Change-Id: Ie0fc97d4009c1c70a1ae170d8182cc6c84e7901a
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/60570
Commit-Queue: Lei Zhang <thestig@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
diff --git a/core/fxcrt/fx_system.cpp b/core/fxcrt/fx_system.cpp
index 5f162bd..0eb5b00 100644
--- a/core/fxcrt/fx_system.cpp
+++ b/core/fxcrt/fx_system.cpp
@@ -98,6 +98,16 @@
   return static_cast<int>(round(f));
 }
 
+int FXSYS_round(double d) {
+  if (std::isnan(d))
+    return 0;
+  if (d < static_cast<double>(std::numeric_limits<int>::min()))
+    return std::numeric_limits<int>::min();
+  if (d >= static_cast<double>(std::numeric_limits<int>::max()))
+    return std::numeric_limits<int>::max();
+  return static_cast<int>(round(d));
+}
+
 int32_t FXSYS_atoi(const char* str) {
   return FXSYS_StrToInt<int32_t, char>(str);
 }
diff --git a/core/fxcrt/fx_system.h b/core/fxcrt/fx_system.h
index e6eace6..9963b90 100644
--- a/core/fxcrt/fx_system.h
+++ b/core/fxcrt/fx_system.h
@@ -188,6 +188,7 @@
 int64_t FXSYS_atoi64(const char* str);
 const char* FXSYS_i64toa(int64_t value, char* str, int radix);
 int FXSYS_roundf(float f);
+int FXSYS_round(double d);
 #define FXSYS_sqrt2(a, b) (float)sqrt((a) * (a) + (b) * (b))
 #ifdef __cplusplus
 }  // extern C
diff --git a/core/fxcrt/fx_system_unittest.cpp b/core/fxcrt/fx_system_unittest.cpp
index fd2f96e..bc5dbba 100644
--- a/core/fxcrt/fx_system_unittest.cpp
+++ b/core/fxcrt/fx_system_unittest.cpp
@@ -124,6 +124,57 @@
   EXPECT_EQ(0, FXSYS_roundf(NAN));
 }
 
+TEST(fxcrt, FXSYS_round) {
+  EXPECT_EQ(0, FXSYS_round(0.0));
+  EXPECT_EQ(0, FXSYS_round(-0.0));
+  EXPECT_EQ(0, FXSYS_round(0.00001));
+  EXPECT_EQ(0, FXSYS_round(-0.00001));
+  EXPECT_EQ(3, FXSYS_round(3.14159));
+  EXPECT_EQ(4, FXSYS_round(3.5));
+
+  // Check for smallest non-zero double values.
+  EXPECT_EQ(0, FXSYS_round(std::numeric_limits<double>::min()));
+  EXPECT_EQ(0, FXSYS_round(-std::numeric_limits<double>::min()));
+
+  // Function is a wrapper around standard C library function round(), so
+  // returns the integral value that is nearest to x, with halfway cases
+  // rounded away from zero.
+  EXPECT_EQ(-3, FXSYS_round(-3.14159));
+  EXPECT_EQ(-4, FXSYS_round(-3.5));
+
+  // Positive rounding stops at maximum int.
+  // MAX_INT=0x7FFFFFFF=2147483647=2.147483647e+9
+  // In IEEE-754 double precision format, 2^31 yields exponent of 0x41E with
+  // mantissa of all zeroes which is 0x41E0000000000000=2.14748365e+9, which
+  // is beyond max integer.
+  // Going to next smallest float by minus one from exponent and mantissa of
+  // all ones yields binary float representation of
+  // 41DFFFFFFFC00000=2.147483647e+9, which matches the max integer.
+  EXPECT_EQ(2147483647, FXSYS_round(2.147483647e+9));
+
+  // Using a slightly larger value, expect to see it be capped at MAX_INT.
+  EXPECT_EQ(2147483647, FXSYS_round(2.14748365e+9));
+
+  EXPECT_EQ(2147483647, FXSYS_round(2.14748365e+10));
+  EXPECT_EQ(2147483647, FXSYS_round(std::numeric_limits<double>::max()));
+
+  // Negative rounding stops at minimum int.
+  // MIN_INT=0x80000000=-2147483648,=-2.147483648e+9
+  // In IEEE-754 double precision format, 2^31 yields exponent of 0x41E with
+  // mantissa of all zeroes which is 0x41E0000000000000=2.14748365e+9, and the
+  // sign bit set, which is 0xC1E0000000000000 and exactly matches the minimum
+  // integer.  Going to next smallest negative double by minus one from
+  // exponent and mantissa of all ones yields binary float representation of
+  // 0xC1DFFFFFFFFFFFFF=-2.1474836479999998e+9, which is -2147483648.
+  EXPECT_EQ(-2147483648, FXSYS_round(-2.1474836479999998e+9));
+  EXPECT_EQ(-2147483648, FXSYS_round(-2.147483648e+9));
+  EXPECT_EQ(-2147483648, FXSYS_round(-2.147483648e+10));
+  EXPECT_EQ(-2147483648, FXSYS_round(-std::numeric_limits<double>::max()));
+
+  // NaN should give zero.
+  EXPECT_EQ(0, FXSYS_round(NAN));
+}
+
 TEST(fxcrt, FXSYS_itoa_InvalidRadix) {
   char buf[32];