test_float.cxx 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. #include <cmath>
  2. #include <pqxx/transaction>
  3. #include "../test_helpers.hxx"
  4. namespace
  5. {
  6. /// Test conversions for some floating-point type.
  7. template<typename T> void infinity_test()
  8. {
  9. T inf{std::numeric_limits<T>::infinity()};
  10. std::string inf_string;
  11. T back_conversion;
  12. inf_string = pqxx::to_string(inf);
  13. pqxx::from_string(inf_string, back_conversion);
  14. PQXX_CHECK_LESS(
  15. T(999999999), back_conversion,
  16. "Infinity doesn't convert back to something huge.");
  17. inf_string = pqxx::to_string(-inf);
  18. pqxx::from_string(inf_string, back_conversion);
  19. PQXX_CHECK_LESS(
  20. back_conversion, -T(999999999), "Negative infinity is broken");
  21. }
  22. void test_infinities()
  23. {
  24. infinity_test<float>();
  25. infinity_test<double>();
  26. infinity_test<long double>();
  27. }
  28. /// Reproduce bug #262: repeated float conversions break without charconv.
  29. template<typename T> void bug_262()
  30. {
  31. pqxx::connection conn;
  32. conn.prepare("stmt", "select cast($1 as float)");
  33. pqxx::work tr{conn};
  34. // We must use the same float type both for passing the value to the
  35. // statement and for retrieving result of the statement execution. This is
  36. // due to an internal stringstream being instantiated as a a parameterized
  37. // thread-local singleton. So, there are separate stream<float>,
  38. // stream<double>, stream<long double>, but every such instance is a
  39. // singleton. We should use only one of them for this test.
  40. pqxx::row row;
  41. // Nothing bad here, select a float value.
  42. // The stream is clear, so just fill it with the value and extract str().
  43. row = tr.exec1("SELECT 1.0");
  44. // This works properly, but as we parse the value from the stream, the
  45. // seeking cursor moves towards the EOF. When the inevitable EOF happens
  46. // 'eof' flag is set in the stream and 'good' flag is unset.
  47. row[0].as<T>();
  48. // The second try. Select a float value again. The stream is not clean, so
  49. // we need to put an empty string into its buffer {stream.str("");}. This
  50. // resets the seeking cursor to 0. Then we will put the value using
  51. // operator<<().
  52. // ...
  53. // ...
  54. // OOPS. stream.str("") does not reset 'eof' flag and 'good' flag! We are
  55. // trying to read from EOF! This is no good.
  56. // Throws on unpatched pqxx v6.4.5
  57. row = tr.exec1("SELECT 2.0");
  58. // We won't get here without patch. The following statements are just for
  59. // demonstration of how are intended to work. If we
  60. // simply just reset the stream flags properly, this would work fine.
  61. // The most obvious patch is just explicitly stream.seekg(0).
  62. row[0].as<T>();
  63. row = tr.exec1("SELECT 3.0");
  64. row[0].as<T>();
  65. }
  66. /// Test for bug #262.
  67. void test_bug_262()
  68. {
  69. bug_262<float>();
  70. bug_262<double>();
  71. bug_262<long double>();
  72. }
  73. /// Test conversion of malformed floating-point values.
  74. void test_bad_float()
  75. {
  76. float x [[maybe_unused]];
  77. PQXX_CHECK_THROWS(
  78. x = pqxx::from_string<float>(""), pqxx::conversion_error,
  79. "Conversion of empty string to float was not caught.");
  80. PQXX_CHECK_THROWS(
  81. x = pqxx::from_string<float>("Infancy"), pqxx::conversion_error,
  82. "Misleading infinity was not caught.");
  83. PQXX_CHECK_THROWS(
  84. x = pqxx::from_string<float>("-Infighting"), pqxx::conversion_error,
  85. "Misleading negative infinity was not caught.");
  86. PQXX_CHECK_THROWS(
  87. x = pqxx::from_string<float>("Nanny"), pqxx::conversion_error,
  88. "Conversion of misleading NaN was not caught.");
  89. }
  90. template<typename T> void test_float_length(T value)
  91. {
  92. auto const text{pqxx::to_string(value)};
  93. PQXX_CHECK_GREATER_EQUAL(
  94. pqxx::size_buffer(value), std::size(text) + 1,
  95. "Not enough buffer space for " + text + ".");
  96. }
  97. /// Test conversion of long float values to strings.
  98. void test_long_float()
  99. {
  100. test_float_length(0.1f);
  101. test_float_length(0.1);
  102. test_float_length(std::numeric_limits<float>::denorm_min());
  103. test_float_length(-std::numeric_limits<float>::denorm_min());
  104. test_float_length(std::numeric_limits<float>::min());
  105. test_float_length(-std::numeric_limits<float>::min());
  106. test_float_length(std::numeric_limits<float>::max());
  107. test_float_length(-std::numeric_limits<float>::max());
  108. test_float_length(-std::nextafter(1.0f, 2.0f));
  109. test_float_length(std::numeric_limits<double>::denorm_min());
  110. test_float_length(-std::numeric_limits<double>::denorm_min());
  111. test_float_length(std::numeric_limits<double>::min());
  112. test_float_length(-std::numeric_limits<double>::min());
  113. test_float_length(std::numeric_limits<double>::max());
  114. test_float_length(-std::numeric_limits<double>::max());
  115. test_float_length(-std::nextafter(1.0, 2.0));
  116. test_float_length(std::numeric_limits<long double>::denorm_min());
  117. test_float_length(-std::numeric_limits<long double>::denorm_min());
  118. test_float_length(std::numeric_limits<long double>::min());
  119. test_float_length(-std::numeric_limits<long double>::min());
  120. test_float_length(std::numeric_limits<long double>::max());
  121. test_float_length(-std::numeric_limits<long double>::max());
  122. test_float_length(-std::nextafter(1.0L, 2.0L));
  123. // Ahem. I'm not proud of this. We really can't assume much about the
  124. // floating-point types, but I'd really like to try a few things to see that
  125. // buffer sizes are in the right ballpark. So, if "double" is at least 64
  126. // bits, check for some examples of long conversions.
  127. if constexpr (sizeof(double) >= 8)
  128. {
  129. auto constexpr awkward{-2.2250738585072014e-308};
  130. auto const text{pqxx::to_string(awkward)};
  131. PQXX_CHECK_LESS_EQUAL(
  132. std::size(text), 25u, text + " converted to too long a string.");
  133. }
  134. if constexpr (sizeof(double) <= 8)
  135. {
  136. auto const text{pqxx::to_string(0.99)};
  137. PQXX_CHECK_LESS_EQUAL(
  138. pqxx::size_buffer(0.99), 25u, text + " converted to too long a string.");
  139. }
  140. }
  141. PQXX_REGISTER_TEST(test_infinities);
  142. PQXX_REGISTER_TEST(test_bug_262);
  143. PQXX_REGISTER_TEST(test_bad_float);
  144. PQXX_REGISTER_TEST(test_long_float);
  145. } // namespace