OR-Tools  9.6
fp_roundtrip_conv.cc
Go to the documentation of this file.
1 // Copyright 2010-2022 Google LLC
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
15 
16 #include <array>
17 #include <charconv>
18 #include <limits>
19 #include <ostream>
20 #include <string>
21 #include <system_error> // NOLINT(build/c++11)
22 
23 #include "absl/status/statusor.h"
24 #include "absl/strings/charconv.h"
25 #include "absl/strings/escaping.h"
26 #include "absl/strings/str_format.h"
27 #include "absl/strings/string_view.h"
28 #include "ortools/base/logging.h"
30 
31 #define OPERATION_RESEARCH_STD_TO_CHARS_DOUBLE_SUPPORTED
32 #if defined(__wasm__) || defined(ANDROID) || defined(_MSC_VER)
33 #undef OPERATION_RESEARCH_STD_TO_CHARS_DOUBLE_SUPPORTED
34 #endif
35 #if defined(__APPLE__) || defined(__FreeBSD__)
36 #undef OPERATION_RESEARCH_STD_TO_CHARS_DOUBLE_SUPPORTED
37 #endif
38 #if defined(__GNUC__) && !defined(__llvm__) && __GNUC__ < 11
39 #undef OPERATION_RESEARCH_STD_TO_CHARS_DOUBLE_SUPPORTED
40 #endif
41 
42 namespace operations_research {
43 namespace {
44 
45 // When using std::to_chars(), the maximum number of digits for the mantissa is
46 // std::numeric_limits<double>::max_digits10, which is 17. On top of that the
47 // max_exponent10 is 308, which takes at most 3 digits. We also have to take
48 // into account the "e+"/"e-", the "-" sign and the ".". Thus the buffer must be
49 // at least 17 + 3 + 2 + 1 + 1 = 24 bytes long.
50 //
51 // When using absl::SNPrintF() with "%.*g" with `max_digits10` for the
52 // precision, it prints at most `precision + 4` digits. The +4 occurs for
53 // numbers d.ddddd...eX where -1 <= X <= -4 since in that case the number is
54 // printed with some leading zeros (i.e. 0.000dddd...). Thus we must have a
55 // string at least 28 bytes long.
56 //
57 // To be safe we had some margin and use a buffer of 4 * 8 bytes.
58 using RoundTripDoubleBuffer = std::array<char, 32>;
59 
60 // Writes the double to the provided buffer and returns a view on the written
61 // range.
62 absl::string_view RoundTripDoubleToBuffer(const double value,
63  RoundTripDoubleBuffer& buffer) {
64 #ifdef OPERATION_RESEARCH_STD_TO_CHARS_DOUBLE_SUPPORTED
65  const auto result =
66  std::to_chars(buffer.data(), buffer.data() + buffer.size(), value);
67  CHECK(result.ec == std::errc()) << std::make_error_code(result.ec).message();
68  return absl::string_view(buffer.data(), result.ptr - buffer.data());
69 #else // OPERATION_RESEARCH_STD_TO_CHARS_DOUBLE_SUPPORTED
70  // Here we use a version that use enough digits to have roundtrip. We lose the
71  // specification that we use the shortest string though.
72  //
73  // We use absl::SNPrintF() since it does not depend on the locale (contrary to
74  // std::snprintf()).
75  const int written =
76  absl::SNPrintF(buffer.data(), buffer.size(), "%.*g",
77  std::numeric_limits<double>::max_digits10, value);
78  CHECK_GT(written, 0);
79  CHECK_LT(written, buffer.size());
80  return absl::string_view(buffer.data(), written);
81 #endif // OPERATION_RESEARCH_STD_TO_CHARS_DOUBLE_SUPPORTED
82 }
83 
84 } // namespace
85 
86 #ifdef OPERATION_RESEARCH_STD_TO_CHARS_DOUBLE_SUPPORTED
87 ABSL_CONST_INIT const bool kStdToCharsDoubleIsSupported = true;
88 #else
89 ABSL_CONST_INIT const bool kStdToCharsDoubleIsSupported = false;
90 #endif
91 
92 #undef OPERATION_RESEARCH_STD_TO_CHARS_DOUBLE_SUPPORTED
93 
94 std::ostream& operator<<(std::ostream& out,
95  const RoundTripDoubleFormat& format) {
96  RoundTripDoubleBuffer buffer;
97  out << RoundTripDoubleToBuffer(format.value_, buffer);
98  return out;
99 }
100 
101 std::string RoundTripDoubleFormat::ToString(const double value) {
102  RoundTripDoubleBuffer buffer;
103  return std::string(RoundTripDoubleToBuffer(value, buffer));
104 }
105 
106 absl::StatusOr<double> RoundTripDoubleFormat::Parse(
107  const absl::string_view str_value) {
108  const char* const begin = str_value.data();
109  const char* const end = begin + str_value.size();
110  double ret = 0.0;
111  const auto result = absl::from_chars(begin, end, ret);
112  if (result.ec == std::errc()) {
113  if (result.ptr != end) {
115  << '"' << absl::CEscape(str_value)
116  << "\" has unexpected suffix starting at index "
117  << (result.ptr - begin);
118  }
119  return ret;
120  }
121  switch (result.ec) {
122  case std::errc::invalid_argument:
124  << '"' << absl::CEscape(str_value) << "\" is not a valid double";
125  case std::errc::result_out_of_range:
127  << '"' << absl::CEscape(str_value)
128  << "\" does not fit in a double precision float";
129  default:
131  << "parsing of \"" << absl::CEscape(str_value)
132  << "\" failed with an unexpected error: "
133  << std::make_error_code(result.ec).message();
134  }
135 }
136 
137 } // namespace operations_research
static absl::StatusOr< double > Parse(const absl::string_view str_value)
static std::string ToString(double value)
int64_t value
Collection of objects used to extend the Constraint Solver library.
std::ostream & operator<<(std::ostream &out, const Assignment &assignment)
ABSL_CONST_INIT const bool kStdToCharsDoubleIsSupported
StatusBuilder InternalErrorBuilder()
StatusBuilder InvalidArgumentErrorBuilder()
std::optional< int64_t > end