mirror of
https://github.com/tfussell/xlnt.git
synced 2024-03-22 13:11:17 +08:00
bug fixes, move the faster serialisation into the numeric header
serialisation ends up roughly 2x improvement going from sstream to snprintf Run on (4 X 3500 MHz CPU s) CPU Caches: L1 Data 32K (x4) L1 Instruction 32K (x4) L2 Unified 262K (x4) L3 Unified 6291K (x1) ------------------------------------------------------------------------------------------------------------- Benchmark Time CPU Iterations ------------------------------------------------------------------------------------------------------------- RandFloatStrs/double_from_string_sstream 968 ns 977 ns 640000 RandFloatStrs/double_from_string_strtod 272 ns 270 ns 2488889 RandFloatStrs/double_from_string_strtod_fixed 272 ns 270 ns 2488889 RandFloatStrs/double_from_string_strtod_fixed_const_ref 273 ns 270 ns 2488889 RandFloatStrs/double_from_string_std_from_chars 193 ns 195 ns 3446154 RandFloatCommaStrs/double_from_string_strtod_fixed_comma_ref 272 ns 273 ns 2635294 RandFloatCommaStrs/double_from_string_strtod_fixed_comma_const_ref 276 ns 273 ns 2635294 RandFloats/string_from_double_sstream 1311 ns 1318 ns 497778 RandFloats/string_from_double_sstream_cached 1076 ns 1050 ns 640000 RandFloats/string_from_double_snprintf 601 ns 600 ns 1120000 RandFloats/string_from_double_snprintf_fixed 600 ns 600 ns 1120000 RandFloats/string_from_double_std_to_chars 117 ns 117 ns 5600000 RandFloatsComma/string_from_double_snprintf_fixed_comma 600 ns 600 ns 1120000
This commit is contained in:
parent
fbbf7ae767
commit
ee593c2673
|
@ -118,7 +118,7 @@ public:
|
|||
std::string serialise(double d)
|
||||
{
|
||||
char buf[Excel_Digit_Precision + 1]; // need space for trailing '\0'
|
||||
int len = snprintf(buf, sizeof(buf), "%16f", d);
|
||||
int len = snprintf(buf, sizeof(buf), "%.15g", d);
|
||||
if (should_convert_comma)
|
||||
{
|
||||
convert_comma(buf, len);
|
||||
|
@ -158,7 +158,7 @@ BENCHMARK_F(RandFloats, string_from_double_snprintf)
|
|||
while (state.KeepRunning())
|
||||
{
|
||||
char buf[16];
|
||||
int len = snprintf(buf, sizeof(buf), "%16f", get_rand());
|
||||
int len = snprintf(buf, sizeof(buf), "%.15g", get_rand());
|
||||
|
||||
benchmark::DoNotOptimize(
|
||||
std::string(buf, len));
|
||||
|
|
|
@ -34,21 +34,6 @@
|
|||
namespace xlnt {
|
||||
namespace detail {
|
||||
|
||||
/// <summary>
|
||||
/// Takes in any number and outputs a string form of that number which will
|
||||
/// serialise and deserialise without loss of precision
|
||||
/// </summary>
|
||||
template <typename Number>
|
||||
std::string serialize_number_to_string(Number num)
|
||||
{
|
||||
// more digits and excel won't match
|
||||
constexpr int Excel_Digit_Precision = 15; //sf
|
||||
std::stringstream ss;
|
||||
ss.precision(Excel_Digit_Precision);
|
||||
ss << num;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// constexpr abs
|
||||
/// </summary>
|
||||
|
@ -117,45 +102,72 @@ bool float_equals(const LNumber &lhs, const RNumber &rhs,
|
|||
return ((lhs + scaled_fuzz) >= rhs) && ((rhs + scaled_fuzz) >= lhs);
|
||||
}
|
||||
|
||||
struct number_converter
|
||||
class number_serialiser
|
||||
{
|
||||
explicit number_converter()
|
||||
: should_convert_to_comma(std::use_facet<std::numpunct<char>>(std::locale{}).decimal_point() == ',')
|
||||
static constexpr int Excel_Digit_Precision = 15; //sf
|
||||
bool should_convert_comma;
|
||||
|
||||
static void convert_comma_to_pt(char *buf, int len)
|
||||
{
|
||||
char *buf_end = buf + len;
|
||||
char *decimal = std::find(buf, buf_end, ',');
|
||||
if (decimal != buf_end)
|
||||
{
|
||||
*decimal = '.';
|
||||
}
|
||||
}
|
||||
|
||||
static void convert_pt_to_comma(char *buf, size_t len)
|
||||
{
|
||||
char *buf_end = buf + len;
|
||||
char *decimal = std::find(buf, buf_end, '.');
|
||||
if (decimal != buf_end)
|
||||
{
|
||||
*decimal = ',';
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
explicit number_serialiser()
|
||||
: should_convert_comma(std::use_facet<std::numpunct<char>>(std::locale{}).decimal_point() == ',')
|
||||
{
|
||||
}
|
||||
|
||||
double stold(std::string &s) const noexcept
|
||||
std::string serialise(double d) const
|
||||
{
|
||||
char buf[30];
|
||||
int len = snprintf(buf, sizeof(buf), "%.15g", d);
|
||||
if (should_convert_comma)
|
||||
{
|
||||
convert_comma_to_pt(buf, len);
|
||||
}
|
||||
return std::string(buf, len);
|
||||
}
|
||||
|
||||
double deserialise(std::string &s) const noexcept
|
||||
{
|
||||
assert(!s.empty());
|
||||
if (should_convert_to_comma)
|
||||
if (should_convert_comma)
|
||||
{
|
||||
auto decimal_pt = std::find(s.begin(), s.end(), '.');
|
||||
if (decimal_pt != s.end())
|
||||
{
|
||||
*decimal_pt = ',';
|
||||
}
|
||||
// s.data() doesn't have a non-const overload until c++17, hence this little dance
|
||||
convert_pt_to_comma(&s[0], s.size());
|
||||
}
|
||||
return strtod(s.c_str(), nullptr);
|
||||
}
|
||||
|
||||
double stold(const std::string &s) const
|
||||
double deserialise(const std::string &s) const
|
||||
{
|
||||
assert(!s.empty());
|
||||
if (!should_convert_to_comma)
|
||||
if (!should_convert_comma)
|
||||
{
|
||||
return strtod(s.c_str(), nullptr);
|
||||
}
|
||||
std::string copy(s);
|
||||
auto decimal_pt = std::find(copy.begin(), copy.end(), '.');
|
||||
if (decimal_pt != copy.end())
|
||||
{
|
||||
*decimal_pt = ',';
|
||||
}
|
||||
return strtod(copy.c_str(), nullptr);
|
||||
char buf[30];
|
||||
assert(s.size() < std::size(buf));
|
||||
auto copy_end = std::copy(s.begin(), s.end(), buf);
|
||||
convert_pt_to_comma(buf, static_cast<size_t>(copy_end - buf));
|
||||
return strtod(buf, nullptr);
|
||||
}
|
||||
|
||||
private:
|
||||
bool should_convert_to_comma = false;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
|
|
@ -40,17 +40,18 @@ public:
|
|||
|
||||
void test_serialise_number()
|
||||
{
|
||||
xlnt::detail::number_serialiser serialiser;
|
||||
// excel serialises numbers as floating point values with <= 15 digits of precision
|
||||
xlnt_assert(xlnt::detail::serialize_number_to_string(1) == "1");
|
||||
xlnt_assert(serialiser.serialise(1) == "1");
|
||||
// trailing zeroes are ignored
|
||||
xlnt_assert(xlnt::detail::serialize_number_to_string(1.0) == "1");
|
||||
xlnt_assert(xlnt::detail::serialize_number_to_string(1.0f) == "1");
|
||||
xlnt_assert(serialiser.serialise(1.0) == "1");
|
||||
xlnt_assert(serialiser.serialise(1.0f) == "1");
|
||||
// one to 1 relation
|
||||
xlnt_assert(xlnt::detail::serialize_number_to_string(1.23456) == "1.23456");
|
||||
xlnt_assert(xlnt::detail::serialize_number_to_string(1.23456789012345) == "1.23456789012345");
|
||||
xlnt_assert(xlnt::detail::serialize_number_to_string(123456.789012345) == "123456.789012345");
|
||||
xlnt_assert(xlnt::detail::serialize_number_to_string(1.23456789012345e+67) == "1.23456789012345e+67");
|
||||
xlnt_assert(xlnt::detail::serialize_number_to_string(1.23456789012345e-67) == "1.23456789012345e-67");
|
||||
xlnt_assert(serialiser.serialise(1.23456) == "1.23456");
|
||||
xlnt_assert(serialiser.serialise(1.23456789012345) == "1.23456789012345");
|
||||
xlnt_assert(serialiser.serialise(123456.789012345) == "123456.789012345");
|
||||
xlnt_assert(serialiser.serialise(1.23456789012345e+67) == "1.23456789012345e+67");
|
||||
xlnt_assert(serialiser.serialise(1.23456789012345e-67) == "1.23456789012345e-67");
|
||||
}
|
||||
|
||||
void test_float_equals_zero()
|
||||
|
|
Loading…
Reference in New Issue
Block a user