Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
fp_roundtrip_conv.cc
Go to the documentation of this file.
1// Copyright 2010-2024 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#include <type_traits>
23#include <utility>
24
25#include "absl/base/attributes.h"
26#include "absl/log/check.h"
27#include "absl/status/statusor.h"
28#include "absl/strings/charconv.h"
29#include "absl/strings/escaping.h"
30#include "absl/strings/str_format.h"
31#include "absl/strings/string_view.h"
34
35namespace operations_research {
36namespace {
37
38// Template type to test if std::to_chars() has an overload for the given
39// type. This source will use it only with `T = double` but for SFINAE to work
40// we must have a generic version.
41template <typename T, typename = void>
42struct std_to_chars_has_overload : std::false_type {};
43
44template <typename T>
45struct std_to_chars_has_overload<
46 T, std::void_t<decltype(std::to_chars(
47 std::declval<char*>(), std::declval<char*>(), std::declval<T>()))>>
48 : std::true_type {};
49
50// Alias to get std_to_chars_has_overload<T>::value directly.
51template <typename T>
52inline constexpr bool std_to_chars_has_overload_v =
53 std_to_chars_has_overload<T>::value;
54
55// When using std::to_chars(), the maximum number of digits for the mantissa is
56// std::numeric_limits<double>::max_digits10, which is 17. On top of that the
57// max_exponent10 is 308, which takes at most 3 digits. We also have to take
58// into account the "e+"/"e-", the "-" sign and the ".". Thus the buffer must be
59// at least 17 + 3 + 2 + 1 + 1 = 24 bytes long.
60//
61// When using absl::SNPrintF() with "%.*g" with `max_digits10` for the
62// precision, it prints at most `precision + 4` digits. The +4 occurs for
63// numbers d.ddddd...eX where -1 <= X <= -4 since in that case the number is
64// printed with some leading zeros (i.e. 0.000dddd...). Thus we must have a
65// string at least 28 bytes long.
66//
67// To be safe we had some margin and use a buffer of 4 * 8 bytes.
68using RoundTripDoubleBuffer = std::array<char, 32>;
69
70// Writes the double to the provided buffer and returns a view on the written
71// range.
72//
73// We have two overloads selected with std::enable_if; one used when
74// std::to_chars() supports double, the other when not.
75template <
76 typename Double = double,
77 typename std::enable_if_t<std_to_chars_has_overload_v<Double>, bool> = true>
78absl::string_view RoundTripDoubleToBuffer(const Double value,
79 RoundTripDoubleBuffer& buffer) {
80 const auto result =
81 std::to_chars(buffer.data(), buffer.data() + buffer.size(), value);
82 CHECK(result.ec == std::errc()) << std::make_error_code(result.ec).message();
83 return absl::string_view(buffer.data(), result.ptr - buffer.data());
84}
85
86// Overload for the case where std::to_chars() does not support double.
87//
88// Here we use a version that use enough digits to have roundtrip. We lose the
89// specification that we use the shortest string though.
90template <typename Double = double,
91 typename std::enable_if_t<!std_to_chars_has_overload_v<Double>,
92 bool> = true>
93absl::string_view RoundTripDoubleToBuffer(const Double value,
94 RoundTripDoubleBuffer& buffer) {
95 // We use absl::SNPrintF() since it does not depend on the locale (contrary to
96 // std::snprintf()).
97 const int written =
98 absl::SNPrintF(buffer.data(), buffer.size(), "%.*g",
99 std::numeric_limits<double>::max_digits10, value);
100 CHECK_GT(written, 0);
101 CHECK_LT(written, buffer.size());
102 return absl::string_view(buffer.data(), written);
103}
104
105} // namespace
106
107ABSL_CONST_INIT const bool kStdToCharsDoubleIsSupported =
108 std_to_chars_has_overload_v<double>;
109
110std::ostream& operator<<(std::ostream& out,
111 const RoundTripDoubleFormat& format) {
112 RoundTripDoubleBuffer buffer;
113 out << RoundTripDoubleToBuffer(format.value_, buffer);
114 return out;
115}
116
117std::string RoundTripDoubleFormat::ToString(const double value) {
118 RoundTripDoubleBuffer buffer;
119 return std::string(RoundTripDoubleToBuffer(value, buffer));
120}
121
122absl::StatusOr<double> RoundTripDoubleFormat::Parse(
123 const absl::string_view str_value) {
124 const char* const begin = str_value.data();
125 const char* const end = begin + str_value.size();
126 double ret = 0.0;
127 const auto result = absl::from_chars(begin, end, ret);
128 if (result.ec == std::errc()) {
129 if (result.ptr != end) {
131 << '"' << absl::CEscape(str_value)
132 << "\" has unexpected suffix starting at index "
133 << (result.ptr - begin);
134 }
135 return ret;
136 }
137 switch (result.ec) {
138 case std::errc::invalid_argument:
140 << '"' << absl::CEscape(str_value) << "\" is not a valid double";
141 case std::errc::result_out_of_range:
143 << '"' << absl::CEscape(str_value)
144 << "\" does not fit in a double precision float";
145 default:
147 << "parsing of \"" << absl::CEscape(str_value)
148 << "\" failed with an unexpected error: "
149 << std::make_error_code(result.ec).message();
150 }
151}
152
153} // namespace operations_research
static absl::StatusOr< double > Parse(absl::string_view str_value)
static std::string ToString(double value)
int64_t value
In SWIG mode, we don't want anything besides these top-level includes.
std::ostream & operator<<(std::ostream &out, const Assignment &assignment)
ABSL_CONST_INIT const bool kStdToCharsDoubleIsSupported
STL namespace.
StatusBuilder InternalErrorBuilder()
StatusBuilder InvalidArgumentErrorBuilder()
std::optional< int64_t > end