浏览代码

Add some more formatting, including a truncation parameter

Sam Jaffe 4 年之前
父节点
当前提交
718db62da5
共有 7 个文件被更改,包括 29 次插入6 次删除
  1. 1 1
      include/math/bigdecimal.h
  2. 1 1
      include/math/biginteger.h
  3. 7 0
      include/math/number_format.h
  4. 1 1
      src/bigdecimal.cpp
  5. 1 1
      src/biginteger.cpp
  6. 10 1
      src/bignum_helper.cpp
  7. 8 1
      test/bigdecimal_test.cpp

+ 1 - 1
include/math/bigdecimal.h

@@ -75,7 +75,7 @@ public:
   friend bool operator>=(bigdecimal const &, bigdecimal const &);
   friend bool operator>(bigdecimal const &, bigdecimal const &);
 
-  std::string to_string(number_format fmt = {}) const;
+  std::string to_string(number_format const & fmt = default_fmt) const;
 
 private:
   bigdecimal(bool, uint64_t);

+ 1 - 1
include/math/biginteger.h

@@ -60,7 +60,7 @@ public:
   friend biginteger & operator*=(biginteger &, biginteger const &);
   friend biginteger & operator/=(biginteger &, biginteger const &);
   // Output
-  std::string to_string(number_format fmt = {}) const;
+  std::string to_string(number_format const & fmt = default_fmt) const;
   // Comparison
   friend bool operator==(biginteger const &, biginteger const &);
   friend bool operator!=(biginteger const &, biginteger const &);

+ 7 - 0
include/math/number_format.h

@@ -8,10 +8,17 @@
 
 #pragma once
 
+#include <limits>
+
 namespace math {
 
 struct number_format {
   bool separate_thousands;
+  size_t decimal_precision;
 };
 
+constexpr number_format const default_fmt{
+    .separate_thousands = false,
+    .decimal_precision = std::numeric_limits<size_t>::max()};
+
 }

+ 1 - 1
src/bigdecimal.cpp

@@ -271,7 +271,7 @@ bool math::operator>(bigdecimal const & rhs, bigdecimal const & lhs) {
 bigdecimal const bigdecimal::ZERO{0}, bigdecimal::ONE{1},
     bigdecimal::NEGATIVE_ONE{-1};
 
-std::string bigdecimal::to_string(number_format fmt) const {
+std::string bigdecimal::to_string(number_format const & fmt) const {
   size_t const decimal_split = size_t(std::max(0, steps_));
   int32_t const hidden = std::max(0, SEG_DIGITS * (-scale() / SEG_DIGITS));
   size_t const chars = SEG_DIGITS * data.size() + size_t(hidden);

+ 1 - 1
src/biginteger.cpp

@@ -196,7 +196,7 @@ bool math::operator>(biginteger const & rhs, biginteger const & lhs) {
 biginteger const biginteger::ZERO{0}, biginteger::ONE{1},
     biginteger::NEGATIVE_ONE{-1};
 
-std::string biginteger::to_string(number_format fmt) const {
+std::string biginteger::to_string(number_format const & fmt) const {
   std::vector<char> output(biginteger::SEG_DIGITS * data.size() + 2, '\0');
   std::ptrdiff_t idx = 0;
   if (is_negative) {

+ 10 - 1
src/bignum_helper.cpp

@@ -151,14 +151,23 @@ data_type divide(data_type & remainder, data_type const & divisor) {
 }
 
 std::string apply(number_format const & fmt, std::string text) {
+  auto decimal_place = text.find('.');
   if (fmt.separate_thousands) {
     constexpr std::size_t const LEN_THOUSAND{3};
-    auto pos = std::min(text.size(), text.find('.'));
+    auto pos = std::min(text.size(), decimal_place);
     for (pos -= LEN_THOUSAND; pos > LEN_THOUSAND; pos -= LEN_THOUSAND) {
       text.insert(pos, ",");
     }
     if (pos) { text.insert(pos, ","); }
   }
+  if (decimal_place < text.size() &&
+      fmt.decimal_precision != std::numeric_limits<size_t>::max()) {
+    auto pos = decimal_place + 1 + fmt.decimal_precision;
+    if (fmt.decimal_precision > 0 && pos + 1 < text.size()) {
+      if (strchr("56789", text[pos + 1])) { ++text[pos]; }
+    }
+    text.erase(pos, std::string::npos);
+  }
   return text;
 }
 }}

+ 8 - 1
test/bigdecimal_test.cpp

@@ -18,11 +18,18 @@ TEST(BigDecimalTest, ConstructDecimal) {
 }
 
 TEST(BigDecimalTest, FormatsWithCommaOnlyBeforeDecimal) {
-  math::number_format fmt{.separate_thousands = true};
+  math::number_format fmt{.separate_thousands = true,
+                          .decimal_precision = size_t(-1)};
   EXPECT_THAT(math::bigdecimal("1000000.0000000001").to_string(fmt),
               "1,000,000.0000000001");
 }
 
+TEST(BigDecimalTest, CanTruncate) {
+  math::number_format fmt{.decimal_precision = 2};
+  EXPECT_THAT(math::bigdecimal("1000000.0000000001").to_string(fmt),
+              "1000000.00");
+}
+
 TEST(BigDecimalTest, ConstructIntWithScaleAndStep) {
   EXPECT_THAT(math::bigdecimal(1000000000, -2).to_string(), "1000000000");
 }