Google Cloud Spanner C++ Client  1.32.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 {
33 inline namespace SPANNER_CLIENT_NS {
34 class Numeric; // defined below
35 } // namespace SPANNER_CLIENT_NS
36 } // namespace spanner
37 
38 // Internal implementation details that callers should not use.
39 namespace spanner_internal {
40 inline namespace SPANNER_CLIENT_NS {
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 
55 } // namespace SPANNER_CLIENT_NS
56 } // namespace spanner_internal
57 
58 namespace spanner {
59 inline namespace SPANNER_CLIENT_NS {
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.
94  Numeric();
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::SPANNER_CLIENT_NS::MakeNumeric(
136  std::string s);
137  explicit Numeric(std::string rep) : rep_(std::move(rep)) {}
138  std::string rep_; // a valid and canonical NUMERIC representation
139 };
140 
141 /**
142  * Construction from a string, in decimal fixed- or floating-point formats.
143  *
144  * - [-+]?[0-9]+(.[0-9]*)?([eE][-+]?[0-9]+)?
145  * - [-+]?.[0-9]+([eE][-+]?[0-9]+)?
146  *
147  * For example, "0", "-999", "3.141592654", "299792458", "6.02214076e23", etc.
148  * There must be digits either before or after any decimal point.
149  *
150  * Fails on syntax errors or if the conversion would yield a value outside
151  * the NUMERIC range. If the argument has more than `kFracPrec` digits after
152  * the decimal point it will be rounded, with halfway cases rounding away
153  * from zero.
154  */
155 StatusOr<Numeric> MakeNumeric(std::string s);
156 
157 /**
158  * Construction from a double.
159  *
160  * Fails on NaN or any argument outside the NUMERIC value range (including
161  * infinities). If the argument has more than `kFracPrec` digits after the
162  * decimal point it will be rounded, with halfway cases rounding away from
163  * zero.
164  */
165 StatusOr<Numeric> MakeNumeric(double d);
166 
167 /**
168  * Construction from an integer `i`, scaled by 10^`exponent`.
169  *
170  * Fails on any (scaled) argument outside the NUMERIC value range.
171  */
172 template <typename T, typename std::enable_if<
173  std::numeric_limits<T>::is_integer, int>::type = 0>
174 StatusOr<Numeric> MakeNumeric(T i, int exponent = 0) {
175  return spanner_internal::MakeNumeric(spanner_internal::ToString(i), exponent);
176 }
177 
178 /**
179  * Conversion to the closest double value, with possible loss of precision.
180  *
181  * Always succeeds (i.e., can never overflow, assuming a double can hold
182  * values up to 10^(kIntPrec+1)).
183  */
184 inline double ToDouble(Numeric const& n) {
185  return std::atof(n.ToString().c_str());
186 }
187 
188 /**
189  * Conversion to the nearest integer value, scaled by 10^`exponent`.
190  *
191  * Rounds halfway cases away from zero. Fails when the destination type
192  * cannot hold that value.
193  *
194  * @par Example
195  *
196  * @code
197  * spanner::Numeric n = spanner::MakeNumeric(123456789, -2).value();
198  * assert(n.ToString() == "1234567.89");
199  * assert(spanner::ToInteger<int>(n).value() == 1234568);
200  * assert(spanner::ToInteger<int>(n, 2).value() == 123456789);
201  * @endcode
202  */
203 ///@{
204 template <typename T,
205  typename std::enable_if<std::numeric_limits<T>::is_integer &&
206  !std::numeric_limits<T>::is_signed,
207  int>::type = 0>
208 StatusOr<T> ToInteger( // NOLINT(misc-no-recursion)
209  Numeric const& n, int exponent = 0) {
210  std::string const& rep = n.ToString();
211  if (exponent != 0) {
212  auto const en = spanner_internal::MakeNumeric(rep, exponent);
213  return en ? ToInteger<T>(*en, 0) : en.status();
214  }
215  T v = 0;
216  constexpr auto kDigits = "0123456789";
217  constexpr auto kMax = (std::numeric_limits<T>::max)();
218  enum { kIntPart, kFracPart } state = kIntPart;
219  for (auto const ch : rep) {
220  auto const* dp = std::strchr(kDigits, ch);
221  if (state == kFracPart) {
222  if (dp - kDigits >= 5) { // dp != nullptr
223  if (v == kMax) return spanner_internal::DataLoss(rep);
224  v = static_cast<T>(v + 1);
225  }
226  break;
227  }
228  if (dp == nullptr) {
229  if (ch == '-') return spanner_internal::DataLoss(rep);
230  state = kFracPart; // ch == '.'
231  } else {
232  if (v > kMax / 10) return spanner_internal::DataLoss(rep);
233  v = static_cast<T>(v * 10);
234  auto d = static_cast<T>(dp - kDigits);
235  if (v > kMax - d) return spanner_internal::DataLoss(rep);
236  v = static_cast<T>(v + d);
237  }
238  }
239  return v;
240 }
241 
242 template <typename T,
243  typename std::enable_if<std::numeric_limits<T>::is_integer &&
244  std::numeric_limits<T>::is_signed,
245  int>::type = 0>
246 StatusOr<T> ToInteger( // NOLINT(misc-no-recursion)
247  Numeric const& n, int exponent = 0) {
248  std::string const& rep = n.ToString();
249  if (exponent != 0) {
250  auto const en = spanner_internal::MakeNumeric(rep, exponent);
251  return en ? ToInteger<T>(*en, 0) : en.status();
252  }
253  T v = 0;
254  constexpr auto kDigits = "0123456789";
255  constexpr auto kMin = (std::numeric_limits<T>::min)();
256  bool negate = true;
257  enum { kIntPart, kFracPart } state = kIntPart;
258  for (auto const ch : rep) {
259  auto const* dp = std::strchr(kDigits, ch);
260  if (state == kFracPart) {
261  if (dp - kDigits >= 5) { // dp != nullptr
262  if (v == kMin) return spanner_internal::DataLoss(rep);
263  v = static_cast<T>(v - 1);
264  }
265  break;
266  }
267  if (dp == nullptr) {
268  if (ch == '-') {
269  negate = false;
270  } else {
271  state = kFracPart; // ch == '.'
272  }
273  } else {
274  if (v < kMin / 10) return spanner_internal::DataLoss(rep);
275  v = static_cast<T>(v * 10);
276  auto d = static_cast<T>(dp - kDigits);
277  if (v < kMin + d) return spanner_internal::DataLoss(rep);
278  v = static_cast<T>(v - d);
279  }
280  }
281  if (!negate) return v;
282  constexpr auto kMax = (std::numeric_limits<T>::max)();
283  if (kMin != -kMax && v == kMin) return spanner_internal::DataLoss(rep);
284  return static_cast<T>(-v);
285 }
286 ///@}
287 
288 } // namespace SPANNER_CLIENT_NS
289 } // namespace spanner
290 } // namespace cloud
291 } // namespace google
292 
293 #endif // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_SPANNER_NUMERIC_H