You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by am...@apache.org on 2018/03/30 14:19:49 UTC
[trafficserver] branch master updated: Support floating points for
bwformat
This is an automated email from the ASF dual-hosted git repository.
amc pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/master by this push:
new d9a4e9a Support floating points for bwformat
d9a4e9a is described below
commit d9a4e9aceda117697b24c124dbb085d939a9a620
Author: Alan Wang <xf...@gmail.com>
AuthorDate: Mon Mar 26 11:28:08 2018 -0700
Support floating points for bwformat
---
lib/ts/BufferWriter.h | 15 ++
lib/ts/BufferWriterFormat.cc | 244 ++++++++++++++++++---------
lib/ts/unit-tests/test_BufferWriterFormat.cc | 191 +++++++++++++++++++++
3 files changed, 368 insertions(+), 82 deletions(-)
diff --git a/lib/ts/BufferWriter.h b/lib/ts/BufferWriter.h
index 0e9616d..cdce9ec 100644
--- a/lib/ts/BufferWriter.h
+++ b/lib/ts/BufferWriter.h
@@ -493,6 +493,9 @@ namespace bw_fmt
/// Generic integral conversion.
BufferWriter &Format_Integer(BufferWriter &w, BWFSpec const &spec, uintmax_t n, bool negative_p);
+ /// Generic floating point conversion.
+ BufferWriter &Format_Floating(BufferWriter &w, BWFSpec const &spec, double n, bool negative_p);
+
} // bw_fmt
/** Compiled BufferWriter format
@@ -676,6 +679,18 @@ bwformat(BufferWriter &w, BWFSpec const &spec, TextView const &tv)
return bwformat(w, spec, static_cast<string_view>(tv));
}
+inline BufferWriter &
+bwformat(BufferWriter &w, BWFSpec const &spec, double const &d)
+{
+ return d < 0 ? bw_fmt::Format_Floating(w, spec, -d, true) : bw_fmt::Format_Floating(w, spec, d, false);
+}
+
+inline BufferWriter &
+bwformat(BufferWriter &w, BWFSpec const &spec, float const &f)
+{
+ return f < 0 ? bw_fmt::Format_Floating(w, spec, -f, true) : bw_fmt::Format_Floating(w, spec, f, false);
+}
+
/* Integer types.
Due to some oddities for MacOS building, need a bit more template magic here. The underlying
diff --git a/lib/ts/BufferWriterFormat.cc b/lib/ts/BufferWriterFormat.cc
index d660e8f..7c02beb 100644
--- a/lib/ts/BufferWriterFormat.cc
+++ b/lib/ts/BufferWriterFormat.cc
@@ -24,6 +24,9 @@
#include <ts/BufferWriter.h>
#include <ctype.h>
#include <ctime>
+#include <cmath>
+#include <math.h>
+#include <array>
namespace
{
@@ -260,6 +263,8 @@ namespace bw_fmt
{
char UPPER_DIGITS[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
char LOWER_DIGITS[] = "0123456789abcdefghijklmnopqrstuvwxyz";
+ static const std::array<uint64_t, 11> POWERS_OF_TEN = {
+ {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000}};
}
/// Templated radix based conversions. Only a small number of radix are supported
@@ -283,6 +288,49 @@ namespace bw_fmt
return (buff + width) - out;
}
+ template <typename F>
+ void
+ Write_Aligned(BufferWriter &w, F const &f, BWFSpec::Align align, int width, char fill, char neg)
+ {
+ switch (align) {
+ case BWFSpec::Align::LEFT:
+ if (neg)
+ w.write(neg);
+ f();
+ while (width-- > 0)
+ w.write(fill);
+ break;
+ case BWFSpec::Align::RIGHT:
+ while (width-- > 0)
+ w.write(fill);
+ if (neg)
+ w.write(neg);
+ f();
+ break;
+ case BWFSpec::Align::CENTER:
+ for (int i = width / 2; i > 0; --i)
+ w.write(fill);
+ if (neg)
+ w.write(neg);
+ f();
+ for (int i = (width + 1) / 2; i > 0; --i)
+ w.write(fill);
+ break;
+ case BWFSpec::Align::SIGN:
+ if (neg)
+ w.write(neg);
+ while (width-- > 0)
+ w.write(fill);
+ f();
+ break;
+ default:
+ if (neg)
+ w.write(neg);
+ f();
+ break;
+ }
+ }
+
BufferWriter &
Format_Integer(BufferWriter &w, BWFSpec const &spec, uintmax_t i, bool neg_p)
{
@@ -336,10 +384,7 @@ namespace bw_fmt
width -= static_cast<int>(n);
string_view digits{buff + sizeof(buff) - n, n};
- // The idea here is the various pieces have all been assembled, the only difference
- // is the order in which they are written to the output.
- switch (spec._align) {
- case BWFSpec::Align::LEFT:
+ if (spec._align == BWFSpec::Align::SIGN) { // custom for signed case because prefix and digits are seperated.
if (neg)
w.write(neg);
if (prefix1) {
@@ -347,59 +392,123 @@ namespace bw_fmt
if (prefix2)
w.write(prefix2);
}
- w.write(digits);
- while (width-- > 0)
- w.write(spec._fill);
- break;
- case BWFSpec::Align::RIGHT:
while (width-- > 0)
w.write(spec._fill);
- if (neg)
- w.write(neg);
- if (prefix1) {
- w.write(prefix1);
- if (prefix2)
- w.write(prefix2);
- }
w.write(digits);
- break;
- case BWFSpec::Align::CENTER:
- for (int i = width / 2; i > 0; --i)
- w.write(spec._fill);
- if (neg)
- w.write(neg);
- if (prefix1) {
- w.write(prefix1);
- if (prefix2)
- w.write(prefix2);
- }
- w.write(digits);
- for (int i = (width + 1) / 2; i > 0; --i)
- w.write(spec._fill);
- break;
- case BWFSpec::Align::SIGN:
- if (neg)
- w.write(neg);
- if (prefix1) {
- w.write(prefix1);
- if (prefix2)
- w.write(prefix2);
+ } else { // use generic Write_Aligned
+ Write_Aligned(w,
+ [&]() {
+ if (prefix1) {
+ w.write(prefix1);
+ if (prefix2)
+ w.write(prefix2);
+ }
+ w.write(digits);
+ },
+ spec._align, width, spec._fill, neg);
+ }
+ return w;
+ }
+
+ /// Format for floating point values. Seperates floating point into a whole number and a
+ /// fraction. The fraction is converted into an unsigned integer based on the specified
+ /// precision, spec._prec. ie. 3.1415 with precision two is seperated into two unsigned
+ /// integers 3 and 14. The different pieces are assembled and placed into the BufferWriter.
+ /// The default is two decimal places. ie. X.XX. The value is always written in base 10.
+ ///
+ /// format: whole.fraction
+ /// or: left.right
+ BufferWriter &
+ Format_Floating(BufferWriter &w, BWFSpec const &spec, double f, bool neg_p)
+ {
+ static const ts::string_view infinity_bwf{"Inf"};
+ static const ts::string_view nan_bwf{"NaN"};
+ static const ts::string_view zero_bwf{"0"};
+ static const ts::string_view subnormal_bwf{"subnormal"};
+ static const ts::string_view unknown_bwf{"unknown float"};
+
+ // Handle floating values that are not normal
+ if (!std::isnormal(f)) {
+ ts::string_view unnormal;
+ switch (std::fpclassify(f)) {
+ case FP_INFINITE:
+ unnormal = infinity_bwf;
+ break;
+ case FP_NAN:
+ unnormal = nan_bwf;
+ break;
+ case FP_ZERO:
+ unnormal = zero_bwf;
+ break;
+ case FP_SUBNORMAL:
+ unnormal = subnormal_bwf;
+ break;
+ default:
+ unnormal = unknown_bwf;
}
- while (width-- > 0)
- w.write(spec._fill);
- w.write(digits);
- break;
- default:
- if (neg)
- w.write(neg);
- if (prefix1) {
- w.write(prefix1);
- if (prefix2)
- w.write(prefix2);
+
+ w.write(unnormal);
+ return w;
+ }
+
+ uint64_t whole_part = static_cast<uint64_t>(f);
+ if (whole_part == f || spec._prec == 0) { // integral
+ return Format_Integer(w, spec, whole_part, neg_p);
+ }
+
+ static constexpr char dec = '.';
+ double frac;
+ size_t l = 0;
+ size_t r = 0;
+ char whole[std::numeric_limits<double>::digits10 + 1];
+ char fraction[std::numeric_limits<double>::digits10 + 1];
+ char neg = 0;
+ int width = static_cast<int>(spec._min); // amount left to fill.
+ unsigned int precision = (spec._prec == BWFSpec::DEFAULT._prec) ? 2 : spec._prec; // default precision 2
+
+ frac = f - whole_part; // split the number
+
+ if (neg_p) {
+ neg = '-';
+ } else if (spec._sign != '-') {
+ neg = spec._sign;
+ }
+
+ // Shift the floating point based on the precision. Used to convert
+ // trailing fraction into an integer value.
+ uint64_t shift;
+ if (precision < POWERS_OF_TEN.size()) {
+ shift = POWERS_OF_TEN[precision];
+ } else { // not precomputed.
+ shift = POWERS_OF_TEN.back();
+ for (precision -= (POWERS_OF_TEN.size() - 1); precision > 0; --precision) {
+ shift *= 10;
}
- w.write(digits);
- break;
}
+
+ uint64_t frac_part = static_cast<uint64_t>(frac * shift + 0.5 /* rounding */);
+
+ l = bw_fmt::To_Radix<10>(whole_part, whole, sizeof(whole), bw_fmt::LOWER_DIGITS);
+ r = bw_fmt::To_Radix<10>(frac_part, fraction, sizeof(fraction), bw_fmt::LOWER_DIGITS);
+
+ // Clip fill width
+ if (neg)
+ --width;
+ width -= static_cast<int>(l);
+ --width; // '.'
+ width -= static_cast<int>(r);
+
+ string_view whole_digits{whole + sizeof(whole) - l, l};
+ string_view frac_digits{fraction + sizeof(fraction) - r, r};
+
+ Write_Aligned(w,
+ [&]() {
+ w.write(whole_digits);
+ w.write(dec);
+ w.write(frac_digits);
+ },
+ spec._align, width, spec._fill, neg);
+
return w;
}
@@ -415,35 +524,6 @@ namespace bw_fmt
}
}
- template <typename F>
- void
- Write_Aligned(BufferWriter &w, F const &f, BWFSpec::Align align, int width, char fill)
- {
- switch (align) {
- case BWFSpec::Align::LEFT:
- case BWFSpec::Align::SIGN:
- f();
- while (width-- > 0)
- w.write(fill);
- break;
- case BWFSpec::Align::RIGHT:
- while (width-- > 0)
- w.write(fill);
- f();
- break;
- case BWFSpec::Align::CENTER:
- for (int i = width / 2; i > 0; --i)
- w.write(fill);
- f();
- for (int i = (width + 1) / 2; i > 0; --i)
- w.write(fill);
- break;
- default:
- f();
- break;
- }
- }
-
} // bw_fmt
BufferWriter &
@@ -461,10 +541,10 @@ bwformat(BufferWriter &w, BWFSpec const &spec, string_view sv)
w.write(spec._type);
width -= 2;
}
- bw_fmt::Write_Aligned(w, [&w, &sv, digits]() { bw_fmt::Hex_Dump(w, sv, digits); }, spec._align, width, spec._fill);
+ bw_fmt::Write_Aligned(w, [&w, &sv, digits]() { bw_fmt::Hex_Dump(w, sv, digits); }, spec._align, width, spec._fill, 0);
} else {
width -= sv.size();
- bw_fmt::Write_Aligned(w, [&w, &sv]() { w.write(sv); }, spec._align, width, spec._fill);
+ bw_fmt::Write_Aligned(w, [&w, &sv]() { w.write(sv); }, spec._align, width, spec._fill, 0);
}
return w;
}
diff --git a/lib/ts/unit-tests/test_BufferWriterFormat.cc b/lib/ts/unit-tests/test_BufferWriterFormat.cc
index 6431803..b1a5b03 100644
--- a/lib/ts/unit-tests/test_BufferWriterFormat.cc
+++ b/lib/ts/unit-tests/test_BufferWriterFormat.cc
@@ -232,6 +232,197 @@ TEST_CASE("bwstring", "[bwprint][bwstring]")
REQUIRE(s == "32767 .. |e99a18c428cb38d5f260|");
}
+TEST_CASE("BWFormat integral", "[bwprint][bwformat]")
+{
+ ts::LocalBufferWriter<256> bw;
+ ts::BWFSpec spec;
+ uint32_t num = 30;
+ int num_neg = -30;
+
+ // basic
+ bwformat(bw, spec, num);
+ REQUIRE(bw.view() == "30");
+ bw.reduce(0);
+ bwformat(bw, spec, num_neg);
+ REQUIRE(bw.view() == "-30");
+ bw.reduce(0);
+
+ // radix
+ ts::BWFSpec spec_hex;
+ spec_hex._radix_lead_p = true;
+ spec_hex._type = 'x';
+ bwformat(bw, spec_hex, num);
+ REQUIRE(bw.view() == "0x1e");
+ bw.reduce(0);
+
+ ts::BWFSpec spec_dec;
+ spec_dec._type = '0';
+ bwformat(bw, spec_dec, num);
+ REQUIRE(bw.view() == "30");
+ bw.reduce(0);
+
+ ts::BWFSpec spec_bin;
+ spec_bin._radix_lead_p = true;
+ spec_bin._type = 'b';
+ bwformat(bw, spec_bin, num);
+ REQUIRE(bw.view() == "0b11110");
+ bw.reduce(0);
+
+ int one = 1;
+ int two = 2;
+ int three_n = -3;
+ // alignment
+ ts::BWFSpec left;
+ left._align = ts::BWFSpec::Align::LEFT;
+ left._min = 5;
+ ts::BWFSpec right;
+ right._align = ts::BWFSpec::Align::RIGHT;
+ right._min = 5;
+ ts::BWFSpec center;
+ center._align = ts::BWFSpec::Align::CENTER;
+ center._min = 5;
+
+ bwformat(bw, left, one);
+ bwformat(bw, right, two);
+ REQUIRE(bw.view() == "1 2");
+ bwformat(bw, right, two);
+ REQUIRE(bw.view() == "1 2 2");
+ bwformat(bw, center, three_n);
+ REQUIRE(bw.view() == "1 2 2 -3 ");
+}
+
+TEST_CASE("BWFormat floating", "[bwprint][bwformat]")
+{
+ ts::LocalBufferWriter<256> bw;
+ ts::BWFSpec spec;
+
+ bw.reduce(0);
+ bw.print("{}", 3.14);
+ REQUIRE(bw.view() == "3.14");
+ bw.reduce(0);
+ bw.print("{} {:.2} {:.0} ", 32.7, 32.7, 32.7);
+ REQUIRE(bw.view() == "32.70 32.70 32 ");
+ bw.reduce(0);
+ bw.print("{} neg {:.3}", -123.2, -123.2);
+ REQUIRE(bw.view() == "-123.20 neg -123.200");
+ bw.reduce(0);
+ bw.print("zero {} quarter {} half {} 3/4 {}", 0, 0.25, 0.50, 0.75);
+ REQUIRE(bw.view() == "zero 0 quarter 0.25 half 0.50 3/4 0.75");
+ bw.reduce(0);
+ bw.print("long {:.11}", 64.9);
+ REQUIRE(bw.view() == "long 64.90000000000");
+ bw.reduce(0);
+
+ double n = 180.278;
+ double neg = -238.47;
+ bwformat(bw, spec, n);
+ REQUIRE(bw.view() == "180.28");
+ bw.reduce(0);
+ bwformat(bw, spec, neg);
+ REQUIRE(bw.view() == "-238.47");
+ bw.reduce(0);
+
+ spec._prec = 5;
+ bwformat(bw, spec, n);
+ REQUIRE(bw.view() == "180.27800");
+ bw.reduce(0);
+ bwformat(bw, spec, neg);
+ REQUIRE(bw.view() == "-238.47000");
+ bw.reduce(0);
+
+ float f = 1234;
+ float fneg = -1;
+ bwformat(bw, spec, f);
+ REQUIRE(bw.view() == "1234");
+ bw.reduce(0);
+ bwformat(bw, spec, fneg);
+ REQUIRE(bw.view() == "-1");
+ bw.reduce(0);
+ f = 1234.5667;
+ spec._prec = 4;
+ bwformat(bw, spec, f);
+ REQUIRE(bw.view() == "1234.5667");
+ bw.reduce(0);
+
+ bw << 1234 << .567;
+ REQUIRE(bw.view() == "12340.57");
+ bw.reduce(0);
+ bw << f;
+ REQUIRE(bw.view() == "1234.57");
+ bw.reduce(0);
+ bw << n;
+ REQUIRE(bw.view() == "180.28");
+ bw.reduce(0);
+ bw << f << n;
+ REQUIRE(bw.view() == "1234.57180.28");
+ bw.reduce(0);
+
+ double edge = 0.345;
+ spec._prec = 3;
+ bwformat(bw, spec, edge);
+ REQUIRE(bw.view() == "0.345");
+ bw.reduce(0);
+ edge = .1234;
+ bwformat(bw, spec, edge);
+ REQUIRE(bw.view() == "0.123");
+ bw.reduce(0);
+ edge = 1.0;
+ bwformat(bw, spec, edge);
+ REQUIRE(bw.view() == "1");
+ bw.reduce(0);
+
+ // alignment
+ double first = 1.23;
+ double second = 2.35;
+ double third = -3.5;
+ ts::BWFSpec left;
+ left._align = ts::BWFSpec::Align::LEFT;
+ left._min = 5;
+ ts::BWFSpec right;
+ right._align = ts::BWFSpec::Align::RIGHT;
+ right._min = 5;
+ ts::BWFSpec center;
+ center._align = ts::BWFSpec::Align::CENTER;
+ center._min = 5;
+
+ bwformat(bw, left, first);
+ bwformat(bw, right, second);
+ REQUIRE(bw.view() == "1.23 2.35");
+ bwformat(bw, right, second);
+ REQUIRE(bw.view() == "1.23 2.35 2.35");
+ bwformat(bw, center, third);
+ REQUIRE(bw.view() == "1.23 2.35 2.35-3.50");
+ bw.reduce(0);
+
+ double over = 1.4444444;
+ ts::BWFSpec over_min;
+ over_min._prec = 7;
+ over_min._min = 5;
+ bwformat(bw, over_min, over);
+ REQUIRE(bw.view() == "1.4444444");
+ bw.reduce(0);
+
+ // Edge
+ bw.print("{}", (1.0 / 0.0));
+ REQUIRE(bw.view() == "Inf");
+ bw.reduce(0);
+
+ double inf = std::numeric_limits<double>::infinity();
+ bw.print(" {} ", inf);
+ REQUIRE(bw.view() == " Inf ");
+ bw.reduce(0);
+
+ double nan_1 = std::nan("1");
+ bw.print("{} {}", nan_1, nan_1);
+ REQUIRE(bw.view() == "NaN NaN");
+ bw.reduce(0);
+
+ double z = 0.0;
+ bw.print("{} ", z);
+ REQUIRE(bw.view() == "0 ");
+ bw.reduce(0);
+}
+
// Normally there's no point in running the performance tests, but it's worth keeping the code
// for when additional testing needs to be done.
#if 0
--
To stop receiving notification emails like this one, please contact
amc@apache.org.