Google Cloud Spanner C++ Client 2.13.0
A C++ Client Library for Google Cloud Spanner
Loading...
Searching...
No Matches
numeric.h
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// https://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 "absl/strings/string_view.h"
23#include <algorithm>
24#include <cstdlib>
25#include <cstring>
26#include <deque>
27#include <limits>
28#include <ostream>
29#include <string>
30#include <type_traits>
31#include <utility>
32
33namespace google {
34namespace cloud {
35namespace spanner {
36GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
37
38// The supported `Decimal` modes.
39enum class DecimalMode {
40 // kGoogleSQL mode supports:
41 // - 29 decimal digits of integer precision
42 // - 9 decimal digits of fractional precision
44
45 // kPostgreSQL mode supports:
46 // - 131072 decimal digits of integer precision
47 // - 16383 decimal digits of fractional precision
48 // - NaN (not a number)
50};
51
52template <DecimalMode>
53class Decimal; // defined below
54
55GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
56} // namespace spanner
57
58namespace spanner_internal {
59GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
60template <spanner::DecimalMode Mode>
61StatusOr<spanner::Decimal<Mode>> MakeDecimal(std::string);
62GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
63} // namespace spanner_internal
64
65namespace spanner {
66GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
67
68/**
69 * A representation of the Spanner NUMERIC type: an exact decimal value with
70 * a maximum integer precision (kIntPrecision) and rounding to a maximum
71 * fractional precision (kFracPrecision).
72 *
73 * A `Decimal` can be constructed from, and converted to a `std::string`, a
74 * `double`, or any integral type. See the `MakeDecimal()` factory functions,
75 * the `ToString()` member function, and the `ToDouble()`/`ToInteger()`
76 * free functions.
77 *
78 * `Decimal` values can be copied/assigned/moved, compared for equality, and
79 * streamed.
80 *
81 * @par Example
82 *
83 * @code
84 * auto d = spanner::MakeDecimal<spanner::DecimalMode::kGoogleSQL>(42).value();
85 * assert(d.ToString() == "42");
86 * assert(spanner::ToInteger<int>(d).value() == 42);
87 * @endcode
88 */
89template <DecimalMode>
90class Decimal {
91 public:
92 /// @name Integer and fractional precision of a `Decimal` value of the mode.
93 ///@{
94 static std::size_t const kIntPrecision;
95 static std::size_t const kFracPrecision;
96 ///@}
97
98 /// Whether `DecimalMode` supports NaN values.
99 static bool const kHasNaN;
100
101 ///@{
102 /// @name Backwards-compatibility constants.
103 /// @deprecated Only apply to kGoogleSQL mode, and are no longer used in the
104 /// implementation.
105 static constexpr std::size_t kIntPrec = 29;
106 static constexpr std::size_t kFracPrec = 9;
107 ///@}
108
109 /// A zero value.
110 Decimal() : rep_("0") {}
111
112 /// @name Regular value type, supporting copy, assign, move.
113 ///@{
114 Decimal(Decimal&&) noexcept = default;
115 Decimal& operator=(Decimal&&) noexcept = default;
116 Decimal(Decimal const&) = default;
117 Decimal& operator=(Decimal const&) = default;
118 ///@}
119
120 /**
121 * Conversion to a decimal-string representation of the `Decimal` in one
122 * of the following forms:
123 *
124 * - 0 // value == 0
125 * - -?0.[0-9]*[1-9] // 0 < abs(value) < 1
126 * - -?[1-9][0-9]*(.[0-9]*[1-9])? // abs(value) >= 1
127 * - NaN // "not a number" for kPostgreSQL mode
128 *
129 * Note: The string never includes an exponent field.
130 */
131 ///@{
132 std::string const& ToString() const& { return rep_; }
133 std::string&& ToString() && { return std::move(rep_); }
134 ///@}
135
136 /// @name Relational operators
137 ///@{
138 friend bool operator==(Decimal const& a, Decimal const& b) {
139 // Decimal-value equality, which only depends on the canonical
140 // representation, not the mode. The representation may be "NaN"
141 // in kPostgreSQL mode, but unlike typical NaN implementations,
142 // PostgreSQL considers NaN values as equal, so that they may be
143 // sorted. We do the same.
144 return a.rep_ == b.rep_;
145 }
146 friend bool operator!=(Decimal const& a, Decimal const& b) {
147 return !(a == b);
148 }
149 ///@}
150
151 /// Outputs string representation of the `Decimal` to the provided stream.
152 friend std::ostream& operator<<(std::ostream& os, Decimal const& d) {
153 return os << d.ToString();
154 }
155
156 private:
157 template <DecimalMode Mode>
158 friend StatusOr<Decimal<Mode>> spanner_internal::
159#if defined(__GNUC__) && !defined(__clang__)
160 GOOGLE_CLOUD_CPP_NS::
161#endif
162 MakeDecimal(std::string);
163
164 explicit Decimal(std::string rep) : rep_(std::move(rep)) {}
165
166 std::string rep_; // a valid and canonical decimal representation
167};
168
169template <DecimalMode Mode>
170constexpr std::size_t Decimal<Mode>::kIntPrec;
171template <DecimalMode Mode>
172constexpr std::size_t Decimal<Mode>::kFracPrec;
173
174GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
175} // namespace spanner
176
177// Internal implementation details that callers should not use.
178namespace spanner_internal {
179GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
180
181Status DataLoss(std::string message);
182
183// Like `std::to_string`, but also supports Abseil 128-bit integers.
184template <typename T>
185std::string ToString(T&& value) {
186 return std::to_string(std::forward<T>(value));
187}
188std::string ToString(absl::int128 value);
189std::string ToString(absl::uint128 value);
190
191StatusOr<std::string> MakeDecimalRep(std::string s, bool has_nan,
192 std::size_t int_prec,
193 std::size_t frac_prec);
194StatusOr<std::string> MakeDecimalRep(double d);
195
196template <spanner::DecimalMode Mode>
197StatusOr<spanner::Decimal<Mode>> MakeDecimal(std::string s) {
198 auto rep = MakeDecimalRep(std::move(s), spanner::Decimal<Mode>::kHasNaN,
199 spanner::Decimal<Mode>::kIntPrecision,
200 spanner::Decimal<Mode>::kFracPrecision);
201 if (!rep) return std::move(rep).status();
202 return spanner::Decimal<Mode>(*std::move(rep));
203}
204
205// Like `MakeDecimal(s)`, but with an out-of-band exponent.
206template <spanner::DecimalMode Mode>
207StatusOr<spanner::Decimal<Mode>> MakeDecimal(std::string s, int exponent) {
208 if (exponent != 0) s += 'e' + std::to_string(exponent);
209 return MakeDecimal<Mode>(std::move(s));
210}
211
212GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
213} // namespace spanner_internal
214
215namespace spanner {
216GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
217
218/**
219 * Construction from a string, in decimal fixed- or floating-point formats.
220 *
221 * - [-+]?[0-9]+(.[0-9]*)?([eE][-+]?[0-9]+)?
222 * - [-+]?.[0-9]+([eE][-+]?[0-9]+)?
223 * - [Nn][Aa][Nn] // "not a number" for kPostgreSQL mode
224 *
225 * For example, "0", "-999", "3.141592654", "299792458", "6.02214076e23", etc.
226 * There must be digits either before or after any decimal point.
227 *
228 * Fails on syntax errors or if the conversion would yield a value outside
229 * the NUMERIC range. If the argument has more than `kFracPrecision` digits
230 * after the decimal point it will be rounded, with halfway cases rounding
231 * away from zero.
232 */
233template <DecimalMode Mode>
234StatusOr<Decimal<Mode>> MakeDecimal(std::string s) {
235 return spanner_internal::MakeDecimal<Mode>(std::move(s));
236}
237
238/**
239 * Construction from a double.
240 *
241 * Fails on NaN or any argument outside the NUMERIC value range (including
242 * infinities). If the argument has more than `kFracPrecision` digits after
243 * the decimal point it will be rounded, with halfway cases rounding away
244 * from zero.
245 */
246template <DecimalMode Mode>
247StatusOr<Decimal<Mode>> MakeDecimal(double d) {
248 auto rep = spanner_internal::MakeDecimalRep(d);
249 if (!rep) return std::move(rep).status();
250 return spanner_internal::MakeDecimal<Mode>(*std::move(rep));
251}
252
253/**
254 * Construction from an integer `i`, scaled by 10^`exponent`.
255 *
256 * Fails on any (scaled) argument outside the NUMERIC value range.
257 */
258template <
259 typename T, DecimalMode Mode,
260 /// @cond implementation_details
261 typename std::enable_if<std::numeric_limits<T>::is_integer, int>::type = 0
262 /// @endcond
263 >
264StatusOr<Decimal<Mode>> MakeDecimal(T i, int exponent = 0) {
265 return spanner_internal::MakeDecimal<Mode>(spanner_internal::ToString(i),
266 exponent);
267}
268
269/**
270 * Conversion to the closest double value, with possible loss of precision.
271 *
272 * Always succeeds (i.e., can never overflow, assuming a double can hold
273 * values up to 10^(kIntPrecision+1)).
274 */
275template <DecimalMode Mode>
276double ToDouble(Decimal<Mode> const& d) {
277 return std::atof(d.ToString().c_str());
278}
279
280/**
281 * Conversion to the nearest integer value, scaled by 10^`exponent`.
282 *
283 * Rounds halfway cases away from zero. Fails when the destination type
284 * cannot hold that value.
285 *
286 * @par Example
287 *
288 * @code
289 * auto d =
290 * spanner::MakeDecimal<spanner::DecimalMode::kGoogleSQL>(123456789, -2)
291 * .value();
292 * assert(d.ToString() == "1234567.89");
293 * assert(spanner::ToInteger<int>(d).value() == 1234568);
294 * assert(spanner::ToInteger<int>(d, 2).value() == 123456789);
295 * @endcode
296 */
297///@{
298template <typename T, DecimalMode Mode,
299 /// @cond implementation_details
300 typename std::enable_if<std::numeric_limits<T>::is_integer &&
301 !std::numeric_limits<T>::is_signed,
302 int>::type = 0
303 /// @endcond
304 >
305StatusOr<T> ToInteger( // NOLINT(misc-no-recursion)
306 Decimal<Mode> const& d, int exponent = 0) {
307 std::string const& rep = d.ToString();
308 if (exponent != 0) {
309 auto const en = spanner_internal::MakeDecimal<Mode>(rep, exponent);
310 return en ? ToInteger<T>(*en, 0) : en.status();
311 }
312 T v = 0;
313 constexpr auto kDigits = "0123456789";
314 constexpr auto kMax = (std::numeric_limits<T>::max)();
315 enum { kIntPart, kFracPart } state = kIntPart;
316 for (auto const ch : rep) {
317 auto const* dp = std::strchr(kDigits, ch);
318 if (state == kFracPart) {
319 if (dp - kDigits >= 5) { // dp != nullptr
320 if (v == kMax) return spanner_internal::DataLoss(rep);
321 v = static_cast<T>(v + 1);
322 }
323 break;
324 }
325 if (dp == nullptr) {
326 if (ch == '-') return spanner_internal::DataLoss(rep);
327 state = kFracPart; // ch == '.'
328 } else {
329 if (v > kMax / 10) return spanner_internal::DataLoss(rep);
330 v = static_cast<T>(v * 10);
331 auto dv = static_cast<T>(dp - kDigits);
332 if (v > kMax - dv) return spanner_internal::DataLoss(rep);
333 v = static_cast<T>(v + dv);
334 }
335 }
336 return v;
337}
338template <typename T, DecimalMode Mode,
339 /// @cond implementation_details
340 typename std::enable_if<std::numeric_limits<T>::is_integer &&
341 std::numeric_limits<T>::is_signed,
342 int>::type = 0
343 /// @endcond
344 >
345StatusOr<T> ToInteger( // NOLINT(misc-no-recursion)
346 Decimal<Mode> const& d, int exponent = 0) {
347 std::string const& rep = d.ToString();
348 if (exponent != 0) {
349 auto const en = spanner_internal::MakeDecimal<Mode>(rep, exponent);
350 return en ? ToInteger<T>(*en, 0) : en.status();
351 }
352 T v = 0;
353 constexpr auto kDigits = "0123456789";
354 constexpr auto kMin = (std::numeric_limits<T>::min)();
355 bool negate = true;
356 enum { kIntPart, kFracPart } state = kIntPart;
357 for (auto const ch : rep) {
358 auto const* dp = std::strchr(kDigits, ch);
359 if (state == kFracPart) {
360 if (dp - kDigits >= 5) { // dp != nullptr
361 if (v == kMin) return spanner_internal::DataLoss(rep);
362 v = static_cast<T>(v - 1);
363 }
364 break;
365 }
366 if (dp == nullptr) {
367 if (ch == '-') {
368 negate = false;
369 } else {
370 state = kFracPart; // ch == '.'
371 }
372 } else {
373 if (v < kMin / 10) return spanner_internal::DataLoss(rep);
374 v = static_cast<T>(v * 10);
375 auto dv = static_cast<T>(dp - kDigits);
376 if (v < kMin + dv) return spanner_internal::DataLoss(rep);
377 v = static_cast<T>(v - dv);
378 }
379 }
380 if (!negate) return v;
381 constexpr auto kMax = (std::numeric_limits<T>::max)();
382 if (kMin != -kMax && v == kMin) return spanner_internal::DataLoss(rep);
383 return static_cast<T>(-v);
384}
385///@}
386
387/**
388 * Most users only need the `Numeric` or `PgNumeric` specializations of
389 * `Decimal`. For example:
390 *
391 * @code
392 * auto n = spanner::MakeNumeric(42).value();
393 * assert(n.ToString() == "42");
394 * assert(spanner::ToInteger<int>(n).value() == 42);
395 * @endcode
396 */
397///@{
398using Numeric = Decimal<DecimalMode::kGoogleSQL>;
399using PgNumeric = Decimal<DecimalMode::kPostgreSQL>;
400///@}
401
402/**
403 * `MakeNumeric()` factory functions for `Numeric`.
404 */
405///@{
406inline StatusOr<Numeric> MakeNumeric(std::string s) {
408}
409inline StatusOr<Numeric> MakeNumeric(double d) {
411}
412template <
413 typename T,
414 /// @cond implementation_details
415 typename std::enable_if<std::numeric_limits<T>::is_integer, int>::type = 0
416 /// @endcond
417 >
418StatusOr<Numeric> MakeNumeric(T i, int exponent = 0) {
419 return MakeDecimal<T, DecimalMode::kGoogleSQL>(i, exponent);
420}
421///@}
422
423/**
424 * `MakePgNumeric()` factory functions for `PgNumeric`.
425 */
426///@{
427inline StatusOr<PgNumeric> MakePgNumeric(std::string s) {
429}
430inline StatusOr<PgNumeric> MakePgNumeric(double d) {
432}
433template <
434 typename T,
435 /// @cond implementation_details
436 typename std::enable_if<std::numeric_limits<T>::is_integer, int>::type = 0
437 /// @endcond
438 >
439StatusOr<PgNumeric> MakePgNumeric(T i, int exponent = 0) {
440 return MakeDecimal<T, DecimalMode::kPostgreSQL>(i, exponent);
441}
442///@}
443
444GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
445} // namespace spanner
446} // namespace cloud
447} // namespace google
448
449#endif // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_SPANNER_NUMERIC_H
Status && status() &&
friend bool operator==(Decimal const &a, Decimal const &b)
Definition: numeric.h:138
Decimal & operator=(Decimal &&) noexcept=default
static constexpr std::size_t kFracPrec
Definition: numeric.h:106
static constexpr std::size_t kIntPrec
Definition: numeric.h:105
Decimal(Decimal const &)=default
static std::size_t const kIntPrecision
Definition: numeric.h:94
Decimal & operator=(Decimal const &)=default
friend bool operator!=(Decimal const &a, Decimal const &b)
Definition: numeric.h:146
Decimal()
A zero value.
Definition: numeric.h:110
std::string const & ToString() const &
Conversion to a decimal-string representation of the Decimal in one of the following forms:
Definition: numeric.h:132
Decimal(Decimal &&) noexcept=default
static bool const kHasNaN
Whether DecimalMode supports NaN values.
Definition: numeric.h:99
std::string && ToString() &&
Conversion to a decimal-string representation of the Decimal in one of the following forms:
Definition: numeric.h:133
static std::size_t const kFracPrecision
Definition: numeric.h:95
Contains all the Cloud Spanner C++ client types and functions.
Definition: backoff_policy.h:23
StatusOr< Decimal< Mode > > MakeDecimal(double d)
Construction from a double.
Definition: numeric.h:247
StatusOr< Numeric > MakeNumeric(double d)
MakeNumeric() factory functions for Numeric.
Definition: numeric.h:409
DecimalMode
Definition: numeric.h:39
StatusOr< Decimal< Mode > > MakeDecimal(T i, int exponent=0)
Construction from an integer i, scaled by 10^exponent.
Definition: numeric.h:264
StatusOr< PgNumeric > MakePgNumeric(T i, int exponent=0)
MakePgNumeric() factory functions for PgNumeric.
Definition: numeric.h:439
StatusOr< Numeric > MakeNumeric(T i, int exponent=0)
MakeNumeric() factory functions for Numeric.
Definition: numeric.h:418
StatusOr< PgNumeric > MakePgNumeric(std::string s)
MakePgNumeric() factory functions for PgNumeric.
Definition: numeric.h:427
StatusOr< Decimal< Mode > > MakeDecimal(std::string s)
Construction from a string, in decimal fixed- or floating-point formats.
Definition: numeric.h:234
StatusOr< PgNumeric > MakePgNumeric(double d)
MakePgNumeric() factory functions for PgNumeric.
Definition: numeric.h:430
double ToDouble(Decimal< Mode > const &d)
Conversion to the closest double value, with possible loss of precision.
Definition: numeric.h:276
StatusOr< T > ToInteger(Decimal< Mode > const &d, int exponent=0)
Conversion to the nearest integer value, scaled by 10^exponent.
Definition: numeric.h:305
StatusOr< Numeric > MakeNumeric(std::string s)
MakeNumeric() factory functions for Numeric.
Definition: numeric.h:406