mirror of
https://github.com/loot/yaml-cpp.git
synced 2026-07-27 14:13:42 -07:00
Compare commits
22
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
28f93bdec6 | ||
|
|
39f737443b | ||
|
|
73ef0060aa | ||
|
|
7470c2d871 | ||
|
|
8a9a7b74ef | ||
|
|
d45c4fba3e | ||
|
|
61db89741d | ||
|
|
7def2046ff | ||
|
|
1da813f56a | ||
|
|
f878043f12 | ||
|
|
9ce5a25188 | ||
|
|
ebfbf27115 | ||
|
|
06ffaf3104 | ||
|
|
0b945415fd | ||
|
|
82f3c04081 | ||
|
|
d8de96524c | ||
|
|
5d9e4b6251 | ||
|
|
28c0a1bc25 | ||
|
|
72865f7d32 | ||
|
|
f4bca79dc4 | ||
|
|
d4e00bd473 | ||
|
|
bd070a7b76 |
@@ -1,7 +1,7 @@
|
||||
name: Github PR
|
||||
on:
|
||||
push:
|
||||
branches: [ master, merge-key-support ]
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
workflow_dispatch:
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
${{format(matrix.generator != 'Default Generator' && '-G "{0}"' || '', matrix.generator)}}
|
||||
CMAKE_INSTALL_PREFIX: "${{ github.workspace }}/install-prefix"
|
||||
CMAKE_BUILD_TYPE: Debug
|
||||
CMAKE_CXX_FLAGS_DEBUG: ${{ matrix.googletest == 'build' && '-g -D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC' || '-g' }}
|
||||
CMAKE_CXX_FLAGS_DEBUG: ${{ matrix.googletest == 'build' && '-g -D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC' || '-g' }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
|
||||
@@ -64,8 +64,7 @@ jobs:
|
||||
-D CMAKE_CXX_FLAGS_DEBUG="${{ env.CMAKE_CXX_FLAGS_DEBUG }}" \
|
||||
-D YAML_BUILD_SHARED_LIBS=${{ env.YAML_BUILD_SHARED_LIBS }} \
|
||||
-D YAML_USE_SYSTEM_GTEST=${{ env.YAML_USE_SYSTEM_GTEST }} \
|
||||
-D YAML_CPP_BUILD_TESTS=ON \
|
||||
-DYAML_CPP_SUPPORT_MERGE_KEYS=ON
|
||||
-D YAML_CPP_BUILD_TESTS=ON
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
|
||||
+2
-4
@@ -29,7 +29,6 @@ option(YAML_CPP_FORMAT_SOURCE "Format source" ${YAML_CPP_MAIN_PROJECT})
|
||||
option(YAML_CPP_DISABLE_UNINSTALL "Disable uninstallation of yaml-cpp" OFF)
|
||||
option(YAML_USE_SYSTEM_GTEST "Use system googletest if found" OFF)
|
||||
option(YAML_ENABLE_PIC "Use Position-Independent Code " ON)
|
||||
option(YAML_CPP_SUPPORT_MERGE_KEYS "Support YAML merge keys ('<<') in yaml-cpp's executable targets. Use '#define YAML_CPP_SUPPORT_MERGE_KEYS' instead when linking from another project." OFF)
|
||||
|
||||
cmake_dependent_option(YAML_CPP_BUILD_TESTS
|
||||
"Enable yaml-cpp tests" OFF
|
||||
@@ -39,7 +38,7 @@ cmake_dependent_option(YAML_MSVC_SHARED_RT
|
||||
"CMAKE_SYSTEM_NAME MATCHES Windows" OFF)
|
||||
set(YAML_CPP_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/yaml-cpp"
|
||||
CACHE STRING "Path to install the CMake package to")
|
||||
|
||||
|
||||
if (YAML_CPP_FORMAT_SOURCE)
|
||||
find_program(YAML_CPP_CLANG_FORMAT_EXE NAMES clang-format)
|
||||
endif()
|
||||
@@ -134,8 +133,7 @@ target_compile_definitions(yaml-cpp
|
||||
$<$<NOT:$<BOOL:${YAML_BUILD_SHARED_LIBS}>>:YAML_CPP_STATIC_DEFINE>
|
||||
PRIVATE
|
||||
$<${build-windows-dll}:${PROJECT_NAME}_DLL>
|
||||
$<$<NOT:$<BOOL:${YAML_CPP_BUILD_CONTRIB}>>:YAML_CPP_NO_CONTRIB>
|
||||
$<$<BOOL:${YAML_CPP_SUPPORT_MERGE_KEYS}>:YAML_CPP_SUPPORT_MERGE_KEYS>)
|
||||
$<$<NOT:$<BOOL:${YAML_CPP_BUILD_CONTRIB}>>:YAML_CPP_NO_CONTRIB>)
|
||||
|
||||
target_sources(yaml-cpp
|
||||
PRIVATE
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "yaml-cpp/emittermanip.h"
|
||||
#include "yaml-cpp/null.h"
|
||||
#include "yaml-cpp/ostream_wrapper.h"
|
||||
#include "yaml-cpp/fptostring.h"
|
||||
|
||||
namespace YAML {
|
||||
class Binary;
|
||||
@@ -186,7 +187,7 @@ inline Emitter& Emitter::WriteStreamable(T value) {
|
||||
}
|
||||
|
||||
if (!special) {
|
||||
stream << value;
|
||||
stream << FpToString(value, stream.precision());
|
||||
}
|
||||
m_stream << stream.str();
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
#ifndef YAML_H_FPTOSTRING
|
||||
#define YAML_H_FPTOSTRING
|
||||
|
||||
#include "yaml-cpp/dll.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace YAML {
|
||||
// "precision = 0" refers to shortest known unique representation of the value
|
||||
YAML_CPP_API std::string FpToString(float v, size_t precision = 0);
|
||||
YAML_CPP_API std::string FpToString(double v, size_t precision = 0);
|
||||
YAML_CPP_API std::string FpToString(long double v, size_t precision = 0);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "yaml-cpp/node/node.h"
|
||||
#include "yaml-cpp/node/type.h"
|
||||
#include "yaml-cpp/null.h"
|
||||
#include "yaml-cpp/fptostring.h"
|
||||
|
||||
|
||||
namespace YAML {
|
||||
@@ -129,7 +130,7 @@ inner_encode(const T& rhs, std::stringstream& stream){
|
||||
stream << ".inf";
|
||||
}
|
||||
} else {
|
||||
stream << rhs;
|
||||
stream << FpToString(rhs, stream.precision());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -128,23 +128,12 @@ class node {
|
||||
// NOTE: this returns a non-const node so that the top-level Node can wrap
|
||||
// it, and returns a pointer so that it can be nullptr (if there is no such
|
||||
// key).
|
||||
node* value = static_cast<const node_ref&>(*m_pRef).get(key, pMemory);
|
||||
#ifdef YAML_CPP_SUPPORT_MERGE_KEYS
|
||||
if (!value || value->type() == NodeType::Undefined) {
|
||||
return get_value_from_merge_key(key, value, pMemory);
|
||||
}
|
||||
#endif
|
||||
return value;
|
||||
return static_cast<const node_ref&>(*m_pRef).get(key, pMemory);
|
||||
}
|
||||
template <typename Key>
|
||||
node& get(const Key& key, shared_memory_holder pMemory) {
|
||||
node& value = m_pRef->get(key, pMemory);
|
||||
value.add_dependency(*this);
|
||||
#ifdef YAML_CPP_SUPPORT_MERGE_KEYS
|
||||
if (value.type() == NodeType::Undefined) {
|
||||
return *get_value_from_merge_key(key, &value, pMemory);
|
||||
}
|
||||
#endif
|
||||
return value;
|
||||
}
|
||||
template <typename Key>
|
||||
@@ -176,33 +165,6 @@ class node {
|
||||
}
|
||||
|
||||
private:
|
||||
#ifdef YAML_CPP_SUPPORT_MERGE_KEYS
|
||||
template <typename Key>
|
||||
inline node* get_value_from_merge_key(const Key& key, node* currentValue,
|
||||
shared_memory_holder pMemory) const {
|
||||
node* mergeValue =
|
||||
static_cast<const node_ref&>(*m_pRef).get(std::string("<<"), pMemory);
|
||||
if (!mergeValue) {
|
||||
return currentValue;
|
||||
}
|
||||
if (mergeValue->type() == NodeType::Map) {
|
||||
return &mergeValue->get(key, pMemory);
|
||||
}
|
||||
if (mergeValue->type() == NodeType::Sequence) {
|
||||
for (const_node_iterator it = mergeValue->begin();
|
||||
it != mergeValue->end(); ++it) {
|
||||
if (it->pNode && it->pNode->type() == NodeType::Map) {
|
||||
node* value = it->pNode->get(key, pMemory);
|
||||
if (value && value->type() != NodeType::Undefined) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return currentValue;
|
||||
}
|
||||
#endif
|
||||
|
||||
shared_node_ref m_pRef;
|
||||
using nodes = std::set<node*, less>;
|
||||
nodes m_dependencies;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,238 @@
|
||||
#include "yaml-cpp/fptostring.h"
|
||||
#include "contrib/dragonbox.h"
|
||||
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
#include <tuple>
|
||||
|
||||
namespace YAML {
|
||||
namespace detail {
|
||||
namespace fp_formatting {
|
||||
|
||||
/**
|
||||
* Converts a integer into its ASCII digits.
|
||||
*
|
||||
* @param begin/end - a buffer, must be at least 20bytes long.
|
||||
* @param value - input value.
|
||||
* @param width - minimum number of digits, fill with '0' to the left. Must be equal or smaller than the buffer size.
|
||||
* @return - number of digits filled into the buffer (or -1 if preconditions are not meet)
|
||||
*
|
||||
* Example:
|
||||
* std::array<char, 20> buffer;
|
||||
* auto ct = ConvertToChars(buffer.begin(), buffer.end(), 23, 3);
|
||||
* assert(ct = 3);
|
||||
* assert(buffer[0] == '0');
|
||||
* assert(buffer[1] == '2');
|
||||
* assert(buffer[2] == '3');
|
||||
*/
|
||||
int ConvertToChars(char* begin, char* end, size_t value, int width=1) {
|
||||
// precondition of this function (will trigger in debug build)
|
||||
assert(width >= 1);
|
||||
assert(end >= begin); // end must be after begin
|
||||
assert(end-begin >= width); // Buffer must be large enough
|
||||
assert(end-begin >= 20); // 2^64 has 20digits, so at least 20 digits must be available
|
||||
|
||||
// defensive programming, abort if precondition are not met (will trigger in release build)
|
||||
if (width < 1) {
|
||||
return -1;
|
||||
}
|
||||
if (end < begin) {
|
||||
return -1;
|
||||
}
|
||||
if (end-begin < width) {
|
||||
return -1;
|
||||
}
|
||||
if (end-begin < 20) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
// count number of digits, and fill digits array accordingly
|
||||
int digits_ct{};
|
||||
while (value > 0) {
|
||||
char c = value % 10 + '0';
|
||||
value = value / 10;
|
||||
digits_ct += 1;
|
||||
*(end-digits_ct) = c;
|
||||
}
|
||||
while(digits_ct < width) {
|
||||
assert(digits_ct < 64);
|
||||
digits_ct += 1;
|
||||
*(end-digits_ct) = '0';
|
||||
}
|
||||
// move data to the front of the array
|
||||
std::memmove(begin, end-digits_ct, digits_ct);
|
||||
return digits_ct;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a float or double to a string.
|
||||
*
|
||||
* converts a value 'v' to a string. Uses dragonbox for formatting.
|
||||
*/
|
||||
template <typename T>
|
||||
std::string FpToString(T v, int precision = 0) {
|
||||
// hard coded constant, at which exponent should switch to a scientific notation
|
||||
int const lowerExponentThreshold = -5;
|
||||
int const upperExponentThreshold = (precision==0)?6:precision;
|
||||
if (precision == 0) {
|
||||
precision = 6;
|
||||
}
|
||||
|
||||
// dragonbox/to_decimal does not handle value 0, inf, NaN
|
||||
if (v == 0 || std::isinf(v) || std::isnan(v)) {
|
||||
std::stringstream ss;
|
||||
ss.imbue(std::locale("C"));
|
||||
ss << v;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
auto r = jkj::dragonbox::to_decimal(v);
|
||||
|
||||
auto digits = std::array<char, 20>{}; // max digits of size_t is 20.
|
||||
auto digits_ct = ConvertToChars(digits.data(), digits.data() + digits.size(), r.significand);
|
||||
|
||||
// defensive programming, ConvertToChars arguments are invalid
|
||||
if (digits_ct == -1) {
|
||||
std::stringstream ss;
|
||||
ss.imbue(std::locale("C"));
|
||||
ss << v;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
// check if requested precision is lower than
|
||||
// required digits for exact representation
|
||||
if (digits_ct > precision) {
|
||||
auto diff = digits_ct - precision;
|
||||
r.exponent += diff;
|
||||
digits_ct = precision;
|
||||
|
||||
// round numbers if required
|
||||
if (digits[digits_ct] >= '5') {
|
||||
int i{digits_ct-1};
|
||||
digits[i] += 1;
|
||||
while (digits[i] == '9'+1) {
|
||||
digits_ct -= 1;
|
||||
r.exponent += 1;
|
||||
if (i > 0) {
|
||||
digits[i-1] += 1;
|
||||
i -= 1;
|
||||
} else {
|
||||
digits_ct = 1;
|
||||
digits[0] = '1';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::array<char, 28> output_buffer; // max digits of size_t plus sign, a dot and 2 letters for 'e+' or 'e-' and 4 letters for the exponent
|
||||
auto output_ptr = &output_buffer[0];
|
||||
|
||||
// print '-' symbol for negative numbers
|
||||
if (r.is_negative) {
|
||||
*(output_ptr++) = '-';
|
||||
}
|
||||
|
||||
// exponent if only a single non-zero digit is before the decimal point
|
||||
int const exponent = r.exponent + digits_ct - 1;
|
||||
|
||||
// case 1: scientific notation
|
||||
if (exponent >= upperExponentThreshold || exponent <= lowerExponentThreshold) {
|
||||
// print first digit
|
||||
*(output_ptr++) = digits[0];
|
||||
|
||||
// print digits after decimal point
|
||||
if (digits_ct > 1) {
|
||||
*(output_ptr++) = '.';
|
||||
// print significant numbers after decimal point
|
||||
for (int i{1}; i < digits_ct; ++i) {
|
||||
*(output_ptr++) = digits[i];
|
||||
}
|
||||
}
|
||||
*(output_ptr++) = 'e';
|
||||
*(output_ptr++) = (exponent>=0)?'+':'-';
|
||||
auto exp_digits = std::array<char, 20>{};
|
||||
auto exp_digits_ct = ConvertToChars(exp_digits.data(), exp_digits.data() + exp_digits.size(), std::abs(exponent), /*.precision=*/ 2);
|
||||
|
||||
// defensive programming, ConvertToChars arguments are invalid
|
||||
if (exp_digits_ct == -1) {
|
||||
std::stringstream ss;
|
||||
ss.imbue(std::locale("C"));
|
||||
ss << v;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
for (int i{0}; i < exp_digits_ct; ++i) {
|
||||
*(output_ptr++) = exp_digits[i];
|
||||
}
|
||||
|
||||
// case 2: default notation
|
||||
} else {
|
||||
auto const digits_end = digits.begin() + digits_ct;
|
||||
auto digits_iter = digits.begin();
|
||||
|
||||
// print digits before point
|
||||
int const before_decimal_digits = digits_ct + r.exponent;
|
||||
if (before_decimal_digits > 0) {
|
||||
// print digits before point
|
||||
for (int i{0}; i < std::min(before_decimal_digits, digits_ct); ++i) {
|
||||
*(output_ptr++) = *(digits_iter++);
|
||||
}
|
||||
// print trailing zeros before point
|
||||
for (int i{0}; i < before_decimal_digits - digits_ct; ++i) {
|
||||
*(output_ptr++) = '0';
|
||||
}
|
||||
|
||||
// print 0 before point if none where printed before
|
||||
} else {
|
||||
*(output_ptr++) = '0';
|
||||
}
|
||||
|
||||
if (digits_iter != digits_end) {
|
||||
*(output_ptr++) = '.';
|
||||
// print 0 after decimal point, to fill until first digits
|
||||
int const after_decimal_zeros = -digits_ct - r.exponent;
|
||||
for (int i{0}; i < after_decimal_zeros; ++i) {
|
||||
*(output_ptr++) = '0';
|
||||
}
|
||||
|
||||
// print significant numbers after decimal point
|
||||
for (;digits_iter < digits_end; ++digits_iter) {
|
||||
*(output_ptr++) = *digits_iter;
|
||||
}
|
||||
}
|
||||
}
|
||||
*output_ptr = '\0';
|
||||
return std::string{&output_buffer[0], output_ptr};
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
std::string FpToString(float v, size_t precision) {
|
||||
return detail::fp_formatting::FpToString(v, precision);
|
||||
}
|
||||
|
||||
std::string FpToString(double v, size_t precision) {
|
||||
return detail::fp_formatting::FpToString(v, precision);
|
||||
}
|
||||
|
||||
/**
|
||||
* dragonbox only works for floats/doubles not long double
|
||||
*/
|
||||
std::string FpToString(long double v, size_t precision) {
|
||||
std::stringstream ss;
|
||||
ss.imbue(std::locale("C"));
|
||||
if (precision == 0) {
|
||||
precision = std::numeric_limits<long double>::max_digits10;
|
||||
}
|
||||
ss.precision(precision);
|
||||
ss << v;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -41,9 +41,6 @@ target_compile_options(yaml-cpp-tests
|
||||
PRIVATE
|
||||
$<$<CXX_COMPILER_ID:Clang>:-Wno-c99-extensions -Wno-variadic-macros -Wno-sign-compare>
|
||||
$<$<CXX_COMPILER_ID:GNU>:-Wno-variadic-macros -Wno-sign-compare -Wno-narrowing>)
|
||||
target_compile_definitions(yaml-cpp-tests
|
||||
PRIVATE
|
||||
$<$<BOOL:${YAML_CPP_SUPPORT_MERGE_KEYS}>:YAML_CPP_SUPPORT_MERGE_KEYS>)
|
||||
target_link_libraries(yaml-cpp-tests
|
||||
PRIVATE
|
||||
Threads::Threads
|
||||
|
||||
@@ -0,0 +1,242 @@
|
||||
#include "yaml-cpp/fptostring.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace YAML {
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* Helper function, that converts double to string as std::stringstream would do
|
||||
*/
|
||||
template <typename T>
|
||||
static std::string convert_with_stringstream(T v, size_t precision = 0) {
|
||||
std::stringstream ss;
|
||||
if (precision > 0) {
|
||||
ss << std::setprecision(precision);
|
||||
}
|
||||
ss << v;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
// Caution: Test involving 'convert_with_stringstream' are based on std::stringstream
|
||||
// having certain printing behavior, if these changes, the unit test might fail.
|
||||
// This is not a fault of FpToString just a weakness of the way these
|
||||
// tests are constructed
|
||||
|
||||
TEST(FpToStringTest, conversion_double) {
|
||||
// Issue motivating FpToString function,
|
||||
// https://github.com/jbeder/yaml-cpp/issues/1289
|
||||
// Original problem at hand:
|
||||
EXPECT_EQ("34.34", FpToString(34.34));
|
||||
EXPECT_EQ("56.56", FpToString(56.56));
|
||||
EXPECT_EQ("12.12", FpToString(12.12));
|
||||
EXPECT_EQ("78.78", FpToString(78.78));
|
||||
|
||||
// Special challenge with rounding
|
||||
// https://github.com/jbeder/yaml-cpp/issues/1289#issuecomment-2211705536
|
||||
EXPECT_EQ("1.54743e+26", FpToString(1.5474250491e+26f));
|
||||
EXPECT_EQ(convert_with_stringstream(1.5474250491e+26f), FpToString(1.5474250491e+26f));
|
||||
EXPECT_EQ("1.5474251e+26", FpToString(1.5474250491e+26f, 8));
|
||||
|
||||
// prints the same way as std::stringstream
|
||||
EXPECT_EQ(convert_with_stringstream(1.), FpToString(1.));
|
||||
EXPECT_EQ(convert_with_stringstream(1e0), FpToString(1e0));
|
||||
EXPECT_EQ(convert_with_stringstream(1e1), FpToString(1e1));
|
||||
EXPECT_EQ(convert_with_stringstream(1e2), FpToString(1e2));
|
||||
EXPECT_EQ(convert_with_stringstream(1e3), FpToString(1e3));
|
||||
EXPECT_EQ(convert_with_stringstream(1e4), FpToString(1e4));
|
||||
EXPECT_EQ(convert_with_stringstream(1e5), FpToString(1e5));
|
||||
EXPECT_EQ(convert_with_stringstream(1e6), FpToString(1e6));
|
||||
EXPECT_EQ(convert_with_stringstream(1e7), FpToString(1e7));
|
||||
EXPECT_EQ(convert_with_stringstream(1e8), FpToString(1e8));
|
||||
EXPECT_EQ(convert_with_stringstream(1e9), FpToString(1e9));
|
||||
|
||||
// Print by default values below 1e6 without scientific notation
|
||||
EXPECT_EQ("1", FpToString(1.));
|
||||
EXPECT_EQ("1", FpToString(1e0));
|
||||
EXPECT_EQ("10", FpToString(1e1));
|
||||
EXPECT_EQ("100", FpToString(1e2));
|
||||
EXPECT_EQ("1000", FpToString(1e3));
|
||||
EXPECT_EQ("10000", FpToString(1e4));
|
||||
EXPECT_EQ("100000", FpToString(1e5));
|
||||
EXPECT_EQ("1e+06", FpToString(1e6));
|
||||
EXPECT_EQ("1e+07", FpToString(1e7));
|
||||
EXPECT_EQ("1e+08", FpToString(1e8));
|
||||
EXPECT_EQ("1e+09", FpToString(1e9));
|
||||
|
||||
// prints the same way as std::stringstream
|
||||
EXPECT_EQ(convert_with_stringstream(1.), FpToString(1.));
|
||||
EXPECT_EQ(convert_with_stringstream(1e-0), FpToString(1e-0));
|
||||
EXPECT_EQ(convert_with_stringstream(1e-1), FpToString(1e-1));
|
||||
EXPECT_EQ(convert_with_stringstream(1e-2), FpToString(1e-2));
|
||||
EXPECT_EQ(convert_with_stringstream(1e-3), FpToString(1e-3));
|
||||
EXPECT_EQ(convert_with_stringstream(1e-4), FpToString(1e-4));
|
||||
EXPECT_EQ(convert_with_stringstream(1e-5), FpToString(1e-5));
|
||||
EXPECT_EQ(convert_with_stringstream(1e-6), FpToString(1e-6));
|
||||
EXPECT_EQ(convert_with_stringstream(1e-7), FpToString(1e-7));
|
||||
EXPECT_EQ(convert_with_stringstream(1e-8), FpToString(1e-8));
|
||||
EXPECT_EQ(convert_with_stringstream(1e-9), FpToString(1e-9));
|
||||
|
||||
// Print by default values above 1e-5 without scientific notation
|
||||
EXPECT_EQ("1", FpToString(1.));
|
||||
EXPECT_EQ("1", FpToString(1e-0));
|
||||
EXPECT_EQ("0.1", FpToString(1e-1));
|
||||
EXPECT_EQ("0.01", FpToString(1e-2));
|
||||
EXPECT_EQ("0.001", FpToString(1e-3));
|
||||
EXPECT_EQ("0.0001", FpToString(1e-4));
|
||||
EXPECT_EQ("1e-05", FpToString(1e-5));
|
||||
EXPECT_EQ("1e-06", FpToString(1e-6));
|
||||
EXPECT_EQ("1e-07", FpToString(1e-7));
|
||||
EXPECT_EQ("1e-08", FpToString(1e-8));
|
||||
EXPECT_EQ("1e-09", FpToString(1e-9));
|
||||
|
||||
// changing precision has the same effect as std::stringstream
|
||||
EXPECT_EQ(convert_with_stringstream(123., 1), FpToString(123., 1));
|
||||
EXPECT_EQ(convert_with_stringstream(1234567., 7), FpToString(1234567., 7));
|
||||
EXPECT_EQ(convert_with_stringstream(12345.67, 7), FpToString(12345.67, 7));
|
||||
EXPECT_EQ(convert_with_stringstream(1234567e-9, 7), FpToString(1234567e-9, 7));
|
||||
EXPECT_EQ(convert_with_stringstream(1234567e-9, 1), FpToString(1234567e-9, 1));
|
||||
|
||||
// known example that is difficult to round
|
||||
EXPECT_EQ("1", FpToString(0.9999, 2));
|
||||
EXPECT_EQ("-1", FpToString(-0.9999, 2));
|
||||
|
||||
// some more random tests
|
||||
EXPECT_EQ("1.25", FpToString(1.25));
|
||||
EXPECT_EQ("34.34", FpToString(34.34));
|
||||
EXPECT_EQ("1e+20", FpToString(1e+20));
|
||||
EXPECT_EQ("1.1e+20", FpToString(1.1e+20));
|
||||
EXPECT_EQ("1e-20", FpToString(1e-20));
|
||||
EXPECT_EQ("1.1e-20", FpToString(1.1e-20));
|
||||
EXPECT_EQ("1e-20", FpToString(0.1e-19));
|
||||
EXPECT_EQ("1.1e-20", FpToString(0.11e-19));
|
||||
|
||||
EXPECT_EQ("19", FpToString(18.9, 2));
|
||||
EXPECT_EQ("20", FpToString(19.9, 2));
|
||||
EXPECT_EQ("2e+01", FpToString(19.9, 1));
|
||||
EXPECT_EQ("1.2e-05", FpToString(1.234e-5, 2));
|
||||
EXPECT_EQ("1.3e-05", FpToString(1.299e-5, 2));
|
||||
|
||||
EXPECT_EQ("-1", FpToString(-1.));
|
||||
EXPECT_EQ("-1.25", FpToString(-1.25));
|
||||
EXPECT_EQ("-34.34", FpToString(-34.34));
|
||||
EXPECT_EQ("-1e+20", FpToString(-1e+20));
|
||||
EXPECT_EQ("-1.1e+20", FpToString(-1.1e+20));
|
||||
EXPECT_EQ("-1e-20", FpToString(-1e-20));
|
||||
EXPECT_EQ("-1.1e-20", FpToString(-1.1e-20));
|
||||
EXPECT_EQ("-1e-20", FpToString(-0.1e-19));
|
||||
EXPECT_EQ("-1.1e-20", FpToString(-0.11e-19));
|
||||
|
||||
EXPECT_EQ("-19", FpToString(-18.9, 2));
|
||||
EXPECT_EQ("-20", FpToString(-19.9, 2));
|
||||
EXPECT_EQ("-2e+01", FpToString(-19.9, 1));
|
||||
EXPECT_EQ("-1.2e-05", FpToString(-1.234e-5, 2));
|
||||
EXPECT_EQ("-1.3e-05", FpToString(-1.299e-5, 2));
|
||||
}
|
||||
|
||||
TEST(FpToStringTest, conversion_float) {
|
||||
// Issue motivating FpToString function,
|
||||
// https://github.com/jbeder/yaml-cpp/issues/1289
|
||||
// Original problem at hand:
|
||||
EXPECT_EQ("34.34", FpToString(34.34f));
|
||||
EXPECT_EQ("56.56", FpToString(56.56f));
|
||||
EXPECT_EQ("12.12", FpToString(12.12f));
|
||||
EXPECT_EQ("78.78", FpToString(78.78f));
|
||||
|
||||
// prints the same way as std::stringstream
|
||||
EXPECT_EQ(convert_with_stringstream(1.f), FpToString(1.f));
|
||||
EXPECT_EQ(convert_with_stringstream(1e0f), FpToString(1e0f));
|
||||
EXPECT_EQ(convert_with_stringstream(1e1f), FpToString(1e1f));
|
||||
EXPECT_EQ(convert_with_stringstream(1e2f), FpToString(1e2f));
|
||||
EXPECT_EQ(convert_with_stringstream(1e3f), FpToString(1e3f));
|
||||
EXPECT_EQ(convert_with_stringstream(1e4f), FpToString(1e4f));
|
||||
EXPECT_EQ(convert_with_stringstream(1e5f), FpToString(1e5f));
|
||||
EXPECT_EQ(convert_with_stringstream(1e6f), FpToString(1e6f));
|
||||
EXPECT_EQ(convert_with_stringstream(1e7f), FpToString(1e7f));
|
||||
EXPECT_EQ(convert_with_stringstream(1e8f), FpToString(1e8f));
|
||||
EXPECT_EQ(convert_with_stringstream(1e9f), FpToString(1e9f));
|
||||
|
||||
// Print by default values below 1e6 without scientific notation
|
||||
EXPECT_EQ("1", FpToString(1.f));
|
||||
EXPECT_EQ("1", FpToString(1e0f));
|
||||
EXPECT_EQ("10", FpToString(1e1f));
|
||||
EXPECT_EQ("100", FpToString(1e2f));
|
||||
EXPECT_EQ("1000", FpToString(1e3f));
|
||||
EXPECT_EQ("10000", FpToString(1e4f));
|
||||
EXPECT_EQ("100000", FpToString(1e5f));
|
||||
EXPECT_EQ("1e+06", FpToString(1e6f));
|
||||
EXPECT_EQ("1e+07", FpToString(1e7f));
|
||||
EXPECT_EQ("1e+08", FpToString(1e8f));
|
||||
EXPECT_EQ("1e+09", FpToString(1e9f));
|
||||
|
||||
// prints the same way as std::stringstream
|
||||
EXPECT_EQ(convert_with_stringstream(1.f), FpToString(1.f));
|
||||
EXPECT_EQ(convert_with_stringstream(1e-0f), FpToString(1e-0f));
|
||||
EXPECT_EQ(convert_with_stringstream(1e-1f), FpToString(1e-1f));
|
||||
EXPECT_EQ(convert_with_stringstream(1e-2f), FpToString(1e-2f));
|
||||
EXPECT_EQ(convert_with_stringstream(1e-3f), FpToString(1e-3f));
|
||||
EXPECT_EQ(convert_with_stringstream(1e-4f), FpToString(1e-4f));
|
||||
EXPECT_EQ(convert_with_stringstream(1e-5f), FpToString(1e-5f));
|
||||
EXPECT_EQ(convert_with_stringstream(1e-6f), FpToString(1e-6f));
|
||||
EXPECT_EQ(convert_with_stringstream(1e-7f), FpToString(1e-7f));
|
||||
EXPECT_EQ(convert_with_stringstream(1e-8f), FpToString(1e-8f));
|
||||
EXPECT_EQ(convert_with_stringstream(1e-9f), FpToString(1e-9f));
|
||||
|
||||
// Print by default values above 1e-5 without scientific notation
|
||||
EXPECT_EQ("1", FpToString(1.f));
|
||||
EXPECT_EQ("1", FpToString(1e-0f));
|
||||
EXPECT_EQ("0.1", FpToString(1e-1f));
|
||||
EXPECT_EQ("0.01", FpToString(1e-2f));
|
||||
EXPECT_EQ("0.001", FpToString(1e-3f));
|
||||
EXPECT_EQ("0.0001", FpToString(1e-4f));
|
||||
EXPECT_EQ("1e-05", FpToString(1e-5f));
|
||||
EXPECT_EQ("1e-06", FpToString(1e-6f));
|
||||
EXPECT_EQ("1e-07", FpToString(1e-7f));
|
||||
EXPECT_EQ("1e-08", FpToString(1e-8f));
|
||||
EXPECT_EQ("1e-09", FpToString(1e-9f));
|
||||
|
||||
// changing precision has the same effect as std::stringstream
|
||||
EXPECT_EQ(convert_with_stringstream(123.f, 1), FpToString(123.f, 1));
|
||||
EXPECT_EQ(convert_with_stringstream(1234567.f, 7), FpToString(1234567.f, 7));
|
||||
EXPECT_EQ(convert_with_stringstream(12345.67f, 7), FpToString(12345.67f, 7));
|
||||
EXPECT_EQ(convert_with_stringstream(1234567e-9f, 7), FpToString(1234567e-9f, 7));
|
||||
EXPECT_EQ(convert_with_stringstream(1234567e-9f, 1), FpToString(1234567e-9f, 1));
|
||||
|
||||
// known example that is difficult to round
|
||||
EXPECT_EQ("1", FpToString(0.9999f, 2));
|
||||
EXPECT_EQ("-1", FpToString(-0.9999f, 2));
|
||||
|
||||
// some more random tests
|
||||
EXPECT_EQ("1.25", FpToString(1.25f));
|
||||
EXPECT_EQ("34.34", FpToString(34.34f));
|
||||
EXPECT_EQ("1e+20", FpToString(1e+20f));
|
||||
EXPECT_EQ("1.1e+20", FpToString(1.1e+20f));
|
||||
EXPECT_EQ("1e-20", FpToString(1e-20f));
|
||||
EXPECT_EQ("1.1e-20", FpToString(1.1e-20f));
|
||||
EXPECT_EQ("1e-20", FpToString(0.1e-19f));
|
||||
EXPECT_EQ("1.1e-20", FpToString(0.11e-19f));
|
||||
|
||||
EXPECT_EQ("19", FpToString(18.9f, 2));
|
||||
EXPECT_EQ("20", FpToString(19.9f, 2));
|
||||
EXPECT_EQ("2e+01", FpToString(19.9f, 1));
|
||||
EXPECT_EQ("1.2e-05", FpToString(1.234e-5f, 2));
|
||||
EXPECT_EQ("1.3e-05", FpToString(1.299e-5f, 2));
|
||||
|
||||
EXPECT_EQ("-1", FpToString(-1.f));
|
||||
EXPECT_EQ("-1.25", FpToString(-1.25f));
|
||||
EXPECT_EQ("-34.34", FpToString(-34.34f));
|
||||
EXPECT_EQ("-1e+20", FpToString(-1e+20f));
|
||||
EXPECT_EQ("-1.1e+20", FpToString(-1.1e+20f));
|
||||
EXPECT_EQ("-1e-20", FpToString(-1e-20f));
|
||||
EXPECT_EQ("-1.1e-20", FpToString(-1.1e-20f));
|
||||
EXPECT_EQ("-1e-20", FpToString(-0.1e-19f));
|
||||
EXPECT_EQ("-1.1e-20", FpToString(-0.11e-19f));
|
||||
|
||||
EXPECT_EQ("-19", FpToString(-18.9f, 2));
|
||||
EXPECT_EQ("-20", FpToString(-19.9f, 2));
|
||||
EXPECT_EQ("-2e+01", FpToString(-19.9f, 1));
|
||||
EXPECT_EQ("-1.2e-05", FpToString(-1.234e-5f, 2));
|
||||
EXPECT_EQ("-1.3e-05", FpToString(-1.299e-5f, 2));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace YAML
|
||||
@@ -124,9 +124,11 @@ TEST_F(EmitterTest, NumberPrecision) {
|
||||
out << 3.1425926f;
|
||||
out << 53.5893;
|
||||
out << 2384626.4338;
|
||||
out << 1999926.4338;
|
||||
out << 9999926.4338;
|
||||
out << EndSeq;
|
||||
|
||||
ExpectEmit("- 3.14\n- 54\n- 2.4e+06");
|
||||
ExpectEmit("- 3.14\n- 54\n- 2.4e+06\n- 2e+06\n- 1e+07");
|
||||
}
|
||||
|
||||
TEST_F(EmitterTest, SimpleSeq) {
|
||||
|
||||
@@ -224,57 +224,6 @@ TEST(LoadNodeTest, DereferenceIteratorError) {
|
||||
EXPECT_THROW(node.begin()->begin()->Type(), InvalidNode);
|
||||
}
|
||||
|
||||
#ifdef YAML_CPP_SUPPORT_MERGE_KEYS
|
||||
TEST(NodeTest, MergeKeyScalarSupport) {
|
||||
Node node = Load("{<<: {a: 1}}");
|
||||
ASSERT_FALSE(!node["a"]);
|
||||
EXPECT_EQ(1, node["a"].as<int>());
|
||||
}
|
||||
|
||||
TEST(NodeTest, MergeKeyExistingKey) {
|
||||
Node node = Load("{a: 1, <<: {a: 2}}");
|
||||
ASSERT_FALSE(!node["a"]);
|
||||
EXPECT_EQ(1, node["a"].as<int>());
|
||||
}
|
||||
|
||||
TEST(NodeTest, MergeKeySequenceSupport) {
|
||||
Node node = Load("<<: [{a: 1}, {a: 2, b: 3}]");
|
||||
ASSERT_FALSE(!node["a"]);
|
||||
ASSERT_FALSE(!node["b"]);
|
||||
EXPECT_EQ(1, node["a"].as<int>());
|
||||
EXPECT_EQ(3, node["b"].as<int>());
|
||||
}
|
||||
|
||||
TEST(NodeTest, NestedMergeKeys) {
|
||||
Node node = Load("{<<: {<<: {a: 1}}}");
|
||||
ASSERT_FALSE(!node["a"]);
|
||||
EXPECT_EQ(1, node["a"].as<int>());
|
||||
}
|
||||
|
||||
TEST(NodeTest, AnchorAndMergeKey) {
|
||||
Node node = YAML::Load(R"(
|
||||
a_root: &root_anchor
|
||||
key1: value1
|
||||
key2: value2
|
||||
b_child:
|
||||
<<: *root_anchor
|
||||
key2: value2_override
|
||||
)");
|
||||
|
||||
ASSERT_FALSE(!node["a_root"]);
|
||||
ASSERT_FALSE(!node["b_child"]);
|
||||
EXPECT_EQ("value1", node["a_root"]["key1"].as<std::string>());
|
||||
EXPECT_EQ("value2", node["a_root"]["key2"].as<std::string>());
|
||||
EXPECT_EQ("value1", node["b_child"]["key1"].as<std::string>());
|
||||
EXPECT_EQ("value2_override", node["b_child"]["key2"].as<std::string>());
|
||||
}
|
||||
#else
|
||||
TEST(NodeTest, MergeKeySupport) {
|
||||
Node node = Load("{<<: {a: 1}}");
|
||||
ASSERT_FALSE(node["a"]);
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(NodeTest, EmitEmptyNode) {
|
||||
Node node;
|
||||
Emitter emitter;
|
||||
|
||||
@@ -757,8 +757,15 @@ TEST_F(NodeEmitterTest, SimpleFlowSeqNode) {
|
||||
node.push_back(1.5);
|
||||
node.push_back(2.25);
|
||||
node.push_back(3.125);
|
||||
node.push_back(34.34);
|
||||
node.push_back(56.56);
|
||||
node.push_back(12.12);
|
||||
node.push_back(78.78);
|
||||
node.push_back(0.0003);
|
||||
node.push_back(4000.);
|
||||
node.push_back(1.5474251e+26f);
|
||||
|
||||
ExpectOutput("[1.5, 2.25, 3.125]", node);
|
||||
ExpectOutput("[1.5, 2.25, 3.125, 34.34, 56.56, 12.12, 78.78, 0.0003, 4000, 1.5474251e+26]", node);
|
||||
}
|
||||
|
||||
TEST_F(NodeEmitterTest, NestFlowSeqNode) {
|
||||
|
||||
Reference in New Issue
Block a user