Handle creation of CGFAS_Decimals from floats with bigger values.

Alas, the current implementation is 96 bits; we'd need 128 bits to
cover the full range of an IEEE float.

Dividing a float by 1e32 does not produce a 32-bit shift. The current
code "works" because 64 bits are accidentally carried into |plo| with
|phi, pmid| getting 0s (mercifully) from the botched divisions (except
in some large cases) and then redistributed.

Beef up some tests while we're at it.

Change-Id: I01922d368c52008f9e242909820afeaae796da5f
Reviewed-on: https://pdfium-review.googlesource.com/c/50630
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Tom Sepez <tsepez@chromium.org>
diff --git a/xfa/fgas/crt/cfgas_decimal.cpp b/xfa/fgas/crt/cfgas_decimal.cpp
index 4783ca1..9765c93 100644
--- a/xfa/fgas/crt/cfgas_decimal.cpp
+++ b/xfa/fgas/crt/cfgas_decimal.cpp
@@ -272,18 +272,19 @@
 
 CFGAS_Decimal::CFGAS_Decimal(float val, uint8_t scale) {
   float newval = fabs(val);
-  uint64_t phi;
-  uint64_t pmid;
-  uint64_t plo;
-  plo = static_cast<uint64_t>(newval);
-  pmid = static_cast<uint64_t>(newval / 1e32);
-  phi = static_cast<uint64_t>(newval / 1e64);
-  newval = fmod(newval, 1.0f);
+  float divisor = powf(2.0, 64.0f);
+  uint64_t bottom64 = static_cast<uint64_t>(fmodf(newval, divisor));
+  uint64_t top64 = static_cast<uint64_t>(newval / divisor);
+  uint64_t plo = bottom64 & 0xFFFFFFFF;
+  uint64_t pmid = bottom64 >> 32;
+  uint64_t phi = top64 & 0xFFFFFFFF;
+
+  newval = fmodf(newval, 1.0f);
   for (uint8_t iter = 0; iter < scale; iter++) {
     decimal_helper_mul10(phi, pmid, plo);
     newval *= 10;
     plo += static_cast<uint64_t>(newval);
-    newval = fmod(newval, 1.0f);
+    newval = fmodf(newval, 1.0f);
   }
 
   plo += FXSYS_round(newval);
diff --git a/xfa/fgas/crt/cfgas_decimal_unittest.cpp b/xfa/fgas/crt/cfgas_decimal_unittest.cpp
index b1581b5..5b714e3 100644
--- a/xfa/fgas/crt/cfgas_decimal_unittest.cpp
+++ b/xfa/fgas/crt/cfgas_decimal_unittest.cpp
@@ -4,9 +4,9 @@
 
 #include "xfa/fgas/crt/cfgas_decimal.h"
 
+#include <limits>
+
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/ptr_util.h"
-#include "xfa/fxfa/parser/cxfa_localemgr.h"
 
 TEST(CFGAS_Decimal, Empty) {
   CFGAS_Decimal empty;
@@ -14,3 +14,64 @@
   EXPECT_EQ(0.0f, empty.ToFloat());
   EXPECT_EQ(0.0, empty.ToDouble());
 }
+
+TEST(CFGAS_Decimal, FromInt32) {
+  CFGAS_Decimal big(std::numeric_limits<int32_t>::max());
+  CFGAS_Decimal small(std::numeric_limits<int32_t>::min());
+  EXPECT_STREQ(L"2147483647", big.ToWideString().c_str());
+  EXPECT_STREQ(L"-2147483648", small.ToWideString().c_str());
+}
+
+TEST(CFGAS_Decimal, FromUint32) {
+  CFGAS_Decimal big(std::numeric_limits<uint32_t>::max());
+  CFGAS_Decimal small(std::numeric_limits<uint32_t>::min());
+  EXPECT_STREQ(L"4294967295", big.ToWideString().c_str());
+  EXPECT_STREQ(L"0", small.ToWideString().c_str());
+}
+
+TEST(CFGAS_Decimal, FromUint64) {
+  CFGAS_Decimal big(std::numeric_limits<uint64_t>::max());
+  CFGAS_Decimal small(std::numeric_limits<uint64_t>::min());
+  EXPECT_STREQ(L"18446744073709551615", big.ToWideString().c_str());
+  EXPECT_STREQ(L"0", small.ToWideString().c_str());
+}
+
+TEST(CFGAS_Decimal, FromFloat) {
+  WideString big = CFGAS_Decimal(powf(2.0f, 95.0f), 0).ToWideString();
+  WideString big_expected = L"39614081257132168796771975168";
+
+  // Precision may not be the same on all platforms.
+  EXPECT_EQ(big_expected.GetLength(), big.GetLength());
+  EXPECT_STREQ(big_expected.Left(8).c_str(), big.Left(8).c_str());
+
+  WideString tiny = CFGAS_Decimal(1e20f, 0).ToWideString();
+  WideString tiny_expected = L"100000000000000000000";
+  EXPECT_EQ(tiny_expected.GetLength(), tiny.GetLength());
+  EXPECT_STREQ(tiny_expected.Left(8).c_str(), tiny.Left(8).c_str());
+
+  WideString teeny = CFGAS_Decimal(1e14f, 4).ToWideString();
+  WideString teeny_expected = L"100000000000000.0000";
+  EXPECT_EQ(teeny_expected.GetLength(), teeny.GetLength());
+  EXPECT_STREQ(teeny_expected.Left(8).c_str(), teeny.Left(8).c_str());
+}
+
+TEST(CFGAS_Decimal, FromFloatFractional) {
+  WideString case1 = CFGAS_Decimal(123.456f, 10).ToWideString();
+  WideString case1_expected = L"123.4560000000";
+
+  // Precision may not be the same on all platforms.
+  EXPECT_EQ(case1_expected.GetLength(), case1.GetLength());
+  EXPECT_STREQ(case1_expected.Left(8).c_str(), case1.Left(8).c_str());
+}
+
+TEST(CFGAS_Decimal, FromString) {
+  CFGAS_Decimal big(L"100000000000000000000000000");
+  CFGAS_Decimal small(L"-1000000000000000000000000");
+  EXPECT_STREQ(L"100000000000000000000000000", big.ToWideString().c_str());
+  EXPECT_STREQ(L"-1000000000000000000000000", small.ToWideString().c_str());
+}
+
+TEST(CFGAS_Decimal, FromString28Digits) {
+  CFGAS_Decimal frac(L"32109876543210.0123456890123");
+  EXPECT_STREQ(L"32109876543210.0123456890123", frac.ToWideString().c_str());
+}