Google Cloud Spanner C++ Client  1.34.0
A C++ Client Library for Google Cloud Spanner
numeric.h
Go to the documentation of this file.
1 // Copyright 2020 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_SPANNER_NUMERIC_H
16 #define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_SPANNER_NUMERIC_H
17 
18 #include "google/cloud/spanner/version.h"
19 #include "google/cloud/status.h"
20 #include "google/cloud/status_or.h"
21 #include "absl/numeric/int128.h"
22 #include <cstdlib>
23 #include <cstring>
24 #include <limits>
25 #include <ostream>
26 #include <string>
27 #include <type_traits>
28 #include <utility>
29 
30 namespace google {
31 namespace cloud {
32 namespace spanner {
34 class Numeric; // defined below
36 } // namespace spanner
37 
38 // Internal implementation details that callers should not use.
39 namespace spanner_internal {
41 StatusOr<spanner::Numeric> MakeNumeric(std::string s);
42 
43 // Like `std::to_string`, but also supports Abseil 128-bit integers.
44 template <typename T>
45 std::string ToString(T&& value) {
46  return std::to_string(std::forward<T>(value));
47 }
48 std::string ToString(absl::int128 value);
49 std::string ToString(absl::uint128 value);
50 
51 // Forward declarations.
52 Status DataLoss(std::string message);
53 StatusOr<spanner::Numeric> MakeNumeric(std::string s, int exponent);
54 
56 } // namespace spanner_internal
57 
58 namespace spanner {
60 
61 /**
62  * A representation of the Spanner NUMERIC type: an exact numeric value with
63  * 29 decimal digits of integer precision (kIntPrec) and 9 decimal digits of
64  * fractional precision (kFracPrec).
65  *
66  * So, the range of a `Numeric` is -99999999999999999999999999999.999999999
67  * to 99999999999999999999999999999.999999999.
68  *
69  * A `Numeric` can be constructed from, and converted to a `std::string`, a
70  * `double`, or any integral type. See the `MakeNumeric()` factory functions,
71  * the `ToString()` member function, and the `ToDouble()`/`ToInteger()`
72  * free functions.
73  *
74  * `Numeric` values can be copied/assigned/moved, compared for equality, and
75  * streamed.
76  *
77  * @par Example
78  *
79  * @code
80  * spanner::Numeric n = spanner::MakeNumeric(1234).value();
81  * assert(n.ToString() == "1234");
82  * assert(spanner::ToInteger<int>(n).value() == 1234);
83  * @endcode
84  */
85 class Numeric {
86  public:
87  /// Decimal integer and fractional precision of a `Numeric` value.
88  ///@{
89  static constexpr std::size_t kIntPrec = 29;
90  static constexpr std::size_t kFracPrec = 9;
91  ///@}
92 
93  /// A zero value.
95 
96  /// Regular value type, supporting copy, assign, move.
97  ///@{
98  Numeric(Numeric&&) = default;
99  Numeric& operator=(Numeric&&) = default;
100  Numeric(Numeric const&) = default;
101  Numeric& operator=(Numeric const&) = default;
102  ///@}
103 
104  /**
105  * Conversion to a decimal-string representation of the `Numeric` in one
106  * of the following forms:
107  *
108  * - 0 // value == 0
109  * - -?0.[0-9]{0,8}[1-9] // 0 < abs(value) < 1
110  * - -?[1-9][0-9]{0,28}(.[0-9]{0,8}[1-9])? // abs(value) >= 1
111  *
112  * Note: The string never includes an exponent field.
113  */
114  ///@{
115  std::string const& ToString() const& { return rep_; }
116  std::string&& ToString() && { return std::move(rep_); }
117  ///@}
118 
119  /// Relational operators
120  ///@{
121  friend bool operator==(Numeric const& a, Numeric const& b) {
122  return a.rep_ == b.rep_;
123  }
124  friend bool operator!=(Numeric const& a, Numeric const& b) {
125  return !(a == b);
126  }
127  ///@}
128 
129  /// Outputs string representation of the `Numeric` to the provided stream.
130  friend std::ostream& operator<<(std::ostream& os, Numeric const& n) {
131  return os << n.ToString();
132  }
133 
134  private:
135  friend StatusOr<Numeric> spanner_internal::MakeNumeric(std::string s);
136  explicit Numeric(std::string rep) : rep_(std::move(rep)) {}
137  std::string rep_; // a valid and canonical NUMERIC representation
138 };
139 
140 /**
141  * Construction from a string, in decimal fixed- or floating-point formats.
142  *
143  * - [-+]?[0-9]+(.[0-9]*)?([eE][-+]?[0-9]+)?
144  * - [-+]?.[0-9]+([eE][-+]?[0-9]+)?
145  *
146  * For example, "0", "-999", "3.141592654", "299792458", "6.02214076e23", etc.
147  * There must be digits either before or after any decimal point.
148  *
149  * Fails on syntax errors or if the conversion would yield a value outside
150  * the NUMERIC range. If the argument has more than `kFracPrec` digits after
151  * the decimal point it will be rounded, with halfway cases rounding away
152  * from zero.
153  */
154 StatusOr<Numeric> MakeNumeric(std::string s);
155 
156 /**
157  * Construction from a double.
158  *
159  * Fails on NaN or any argument outside the NUMERIC value range (including
160  * infinities). If the argument has more than `kFracPrec` digits after the
161  * decimal point it will be rounded, with halfway cases rounding away from
162  * zero.
163  */
164 StatusOr<Numeric> MakeNumeric(double d);
165 
166 /**
167  * Construction from an integer `i`, scaled by 10^`exponent`.
168  *
169  * Fails on any (scaled) argument outside the NUMERIC value range.
170  */
171 template <typename T, typename std::enable_if<
172  std::numeric_limits<T>::is_integer, int>::type = 0>
173 StatusOr<Numeric> MakeNumeric(T i, int exponent = 0) {
174  return spanner_internal::MakeNumeric(spanner_internal::ToString(i), exponent);
175 }
176 
177 /**
178  * Conversion to the closest double value, with possible loss of precision.
179  *
180  * Always succeeds (i.e., can never overflow, assuming a double can hold
181  * values up to 10^(kIntPrec+1)).
182  */
183 inline double ToDouble(Numeric const& n) {
184  return std::atof(n.ToString().c_str());
185 }
186 
187 /**
188  * Conversion to the nearest integer value, scaled by 10^`exponent`.
189  *
190  * Rounds halfway cases away from zero. Fails when the destination type
191  * cannot hold that value.
192  *
193  * @par Example
194  *
195  * @code
196  * spanner::Numeric n = spanner::MakeNumeric(123456789, -2).value();
197  * assert(n.ToString() == "1234567.89");
198  * assert(spanner::ToInteger<int>(n).value() == 1234568);
199  * assert(spanner::ToInteger<int>(n, 2).value() == 123456789);
200  * @endcode
201  */
202 ///@{
203 template <typename T,
204  typename std::enable_if<std::numeric_limits<T>::is_integer &&
205  !std::numeric_limits<T>::is_signed,
206  int>::type = 0>
207 StatusOr<T> ToInteger( // NOLINT(misc-no-recursion)
208  Numeric const& n, int exponent = 0) {
209  std::string const& rep = n.ToString();
210  if (exponent != 0) {
211  auto const en = spanner_internal::MakeNumeric(rep, exponent);
212  return en ? ToInteger<T>(*en, 0) : en.status();
213  }
214  T v = 0;
215  constexpr auto kDigits = "0123456789";
216  constexpr auto kMax = (std::numeric_limits<T>::max)();
217  enum { kIntPart, kFracPart } state = kIntPart;
218  for (auto const ch : rep) {
219  auto const* dp = std::strchr(kDigits, ch);
220  if (state == kFracPart) {
221  if (dp - kDigits >= 5) { // dp != nullptr
222  if (v == kMax) return spanner_internal::DataLoss(rep);
223  v = static_cast<T>(v + 1);
224  }
225  break;
226  }
227  if (dp == nullptr) {
228  if (ch == '-') return spanner_internal::DataLoss(rep);
229  state = kFracPart; // ch == '.'
230  } else {
231  if (v > kMax / 10) return spanner_internal::DataLoss(rep);
232  v = static_cast<T>(v * 10);
233  auto d = static_cast<T>(dp - kDigits);
234  if (v > kMax - d) return spanner_internal::DataLoss(rep);
235  v = static_cast<T>(v + d);
236  }
237  }
238  return v;
239 }
240 
241 template <typename T,
242  typename std::enable_if<std::numeric_limits<T>::is_integer &&
243  std::numeric_limits<T>::is_signed,
244  int>::type = 0>
245 StatusOr<T> ToInteger( // NOLINT(misc-no-recursion)
246  Numeric const& n, int exponent = 0) {
247  std::string const& rep = n.ToString();
248  if (exponent != 0) {
249  auto const en = spanner_internal::MakeNumeric(rep, exponent);
250  return en ? ToInteger<T>(*en, 0) : en.status();
251  }
252  T v = 0;
253  constexpr auto kDigits = "0123456789";
254  constexpr auto kMin = (std::numeric_limits<T>::min)();
255  bool negate = true;
256  enum { kIntPart, kFracPart } state = kIntPart;
257  for (auto const ch : rep) {
258  auto const* dp = std::strchr(kDigits, ch);
259  if (state == kFracPart) {
260  if (dp - kDigits >= 5) { // dp != nullptr
261  if (v == kMin) return spanner_internal::DataLoss(rep);
262  v = static_cast<T>(v - 1);
263  }
264  break;
265  }
266  if (dp == nullptr) {
267  if (ch == '-') {
268  negate = false;
269  } else {
270  state = kFracPart; // ch == '.'
271  }
272  } else {
273  if (v < kMin / 10) return spanner_internal::DataLoss(rep);
274  v = static_cast<T>(v * 10);
275  auto d = static_cast<T>(dp - kDigits);
276  if (v < kMin + d) return spanner_internal::DataLoss(rep);
277  v = static_cast<T>(v - d);
278  }
279  }
280  if (!negate) return v;
281  constexpr auto kMax = (std::numeric_limits<T>::max)();
282  if (kMin != -kMax && v == kMin) return spanner_internal::DataLoss(rep);
283  return static_cast<T>(-v);
284 }
285 ///@}
286 
288 } // namespace spanner
289 } // namespace cloud
290 } // namespace google
291 
292 #endif // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_SPANNER_NUMERIC_H