]> git.proxmox.com Git - ceph.git/blame - ceph/src/arrow/cpp/src/arrow/util/decimal.h
import quincy 17.2.0
[ceph.git] / ceph / src / arrow / cpp / src / arrow / util / decimal.h
CommitLineData
1d09f67e
TL
1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements. See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership. The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License. You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied. See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18#pragma once
19
20#include <cstdint>
21#include <iosfwd>
22#include <limits>
23#include <string>
24#include <utility>
25
26#include "arrow/result.h"
27#include "arrow/status.h"
28#include "arrow/type_fwd.h"
29#include "arrow/util/basic_decimal.h"
30#include "arrow/util/string_view.h"
31
32namespace arrow {
33
34/// Represents a signed 128-bit integer in two's complement.
35/// Calculations wrap around and overflow is ignored.
36/// The max decimal precision that can be safely represented is
37/// 38 significant digits.
38///
39/// For a discussion of the algorithms, look at Knuth's volume 2,
40/// Semi-numerical Algorithms section 4.3.1.
41///
42/// Adapted from the Apache ORC C++ implementation
43///
44/// The implementation is split into two parts :
45///
46/// 1. BasicDecimal128
47/// - can be safely compiled to IR without references to libstdc++.
48/// 2. Decimal128
49/// - has additional functionality on top of BasicDecimal128 to deal with
50/// strings and streams.
51class ARROW_EXPORT Decimal128 : public BasicDecimal128 {
52 public:
53 /// \cond FALSE
54 // (need to avoid a duplicate definition in Sphinx)
55 using BasicDecimal128::BasicDecimal128;
56 /// \endcond
57
58 /// \brief constructor creates a Decimal128 from a BasicDecimal128.
59 constexpr Decimal128(const BasicDecimal128& value) noexcept // NOLINT runtime/explicit
60 : BasicDecimal128(value) {}
61
62 /// \brief Parse the number from a base 10 string representation.
63 explicit Decimal128(const std::string& value);
64
65 /// \brief Empty constructor creates a Decimal128 with a value of 0.
66 // This is required on some older compilers.
67 constexpr Decimal128() noexcept : BasicDecimal128() {}
68
69 /// Divide this number by right and return the result.
70 ///
71 /// This operation is not destructive.
72 /// The answer rounds to zero. Signs work like:
73 /// 21 / 5 -> 4, 1
74 /// -21 / 5 -> -4, -1
75 /// 21 / -5 -> -4, 1
76 /// -21 / -5 -> 4, -1
77 /// \param[in] divisor the number to divide by
78 /// \return the pair of the quotient and the remainder
79 Result<std::pair<Decimal128, Decimal128>> Divide(const Decimal128& divisor) const {
80 std::pair<Decimal128, Decimal128> result;
81 auto dstatus = BasicDecimal128::Divide(divisor, &result.first, &result.second);
82 ARROW_RETURN_NOT_OK(ToArrowStatus(dstatus));
83 return std::move(result);
84 }
85
86 /// \brief Convert the Decimal128 value to a base 10 decimal string with the given
87 /// scale.
88 std::string ToString(int32_t scale) const;
89
90 /// \brief Convert the value to an integer string
91 std::string ToIntegerString() const;
92
93 /// \brief Cast this value to an int64_t.
94 explicit operator int64_t() const;
95
96 /// \brief Convert a decimal string to a Decimal128 value, optionally including
97 /// precision and scale if they're passed in and not null.
98 static Status FromString(const util::string_view& s, Decimal128* out,
99 int32_t* precision, int32_t* scale = NULLPTR);
100 static Status FromString(const std::string& s, Decimal128* out, int32_t* precision,
101 int32_t* scale = NULLPTR);
102 static Status FromString(const char* s, Decimal128* out, int32_t* precision,
103 int32_t* scale = NULLPTR);
104 static Result<Decimal128> FromString(const util::string_view& s);
105 static Result<Decimal128> FromString(const std::string& s);
106 static Result<Decimal128> FromString(const char* s);
107
108 static Result<Decimal128> FromReal(double real, int32_t precision, int32_t scale);
109 static Result<Decimal128> FromReal(float real, int32_t precision, int32_t scale);
110
111 /// \brief Convert from a big-endian byte representation. The length must be
112 /// between 1 and 16.
113 /// \return error status if the length is an invalid value
114 static Result<Decimal128> FromBigEndian(const uint8_t* data, int32_t length);
115
116 /// \brief Convert Decimal128 from one scale to another
117 Result<Decimal128> Rescale(int32_t original_scale, int32_t new_scale) const {
118 Decimal128 out;
119 auto dstatus = BasicDecimal128::Rescale(original_scale, new_scale, &out);
120 ARROW_RETURN_NOT_OK(ToArrowStatus(dstatus));
121 return std::move(out);
122 }
123
124 /// \brief Convert to a signed integer
125 template <typename T, typename = internal::EnableIfIsOneOf<T, int32_t, int64_t>>
126 Result<T> ToInteger() const {
127 constexpr auto min_value = std::numeric_limits<T>::min();
128 constexpr auto max_value = std::numeric_limits<T>::max();
129 const auto& self = *this;
130 if (self < min_value || self > max_value) {
131 return Status::Invalid("Invalid cast from Decimal128 to ", sizeof(T),
132 " byte integer");
133 }
134 return static_cast<T>(low_bits());
135 }
136
137 /// \brief Convert to a signed integer
138 template <typename T, typename = internal::EnableIfIsOneOf<T, int32_t, int64_t>>
139 Status ToInteger(T* out) const {
140 return ToInteger<T>().Value(out);
141 }
142
143 /// \brief Convert to a floating-point number (scaled)
144 float ToFloat(int32_t scale) const;
145 /// \brief Convert to a floating-point number (scaled)
146 double ToDouble(int32_t scale) const;
147
148 /// \brief Convert to a floating-point number (scaled)
149 template <typename T>
150 T ToReal(int32_t scale) const {
151 return ToRealConversion<T>::ToReal(*this, scale);
152 }
153
154 friend ARROW_EXPORT std::ostream& operator<<(std::ostream& os,
155 const Decimal128& decimal);
156
157 private:
158 /// Converts internal error code to Status
159 Status ToArrowStatus(DecimalStatus dstatus) const;
160
161 template <typename T>
162 struct ToRealConversion {};
163};
164
165template <>
166struct Decimal128::ToRealConversion<float> {
167 static float ToReal(const Decimal128& dec, int32_t scale) { return dec.ToFloat(scale); }
168};
169
170template <>
171struct Decimal128::ToRealConversion<double> {
172 static double ToReal(const Decimal128& dec, int32_t scale) {
173 return dec.ToDouble(scale);
174 }
175};
176
177/// Represents a signed 256-bit integer in two's complement.
178/// The max decimal precision that can be safely represented is
179/// 76 significant digits.
180///
181/// The implementation is split into two parts :
182///
183/// 1. BasicDecimal256
184/// - can be safely compiled to IR without references to libstdc++.
185/// 2. Decimal256
186/// - (TODO) has additional functionality on top of BasicDecimal256 to deal with
187/// strings and streams.
188class ARROW_EXPORT Decimal256 : public BasicDecimal256 {
189 public:
190 /// \cond FALSE
191 // (need to avoid a duplicate definition in Sphinx)
192 using BasicDecimal256::BasicDecimal256;
193 /// \endcond
194
195 /// \brief constructor creates a Decimal256 from a BasicDecimal256.
196 constexpr Decimal256(const BasicDecimal256& value) noexcept : BasicDecimal256(value) {}
197
198 /// \brief Parse the number from a base 10 string representation.
199 explicit Decimal256(const std::string& value);
200
201 /// \brief Empty constructor creates a Decimal256 with a value of 0.
202 // This is required on some older compilers.
203 constexpr Decimal256() noexcept : BasicDecimal256() {}
204
205 /// \brief Convert the Decimal256 value to a base 10 decimal string with the given
206 /// scale.
207 std::string ToString(int32_t scale) const;
208
209 /// \brief Convert the value to an integer string
210 std::string ToIntegerString() const;
211
212 /// \brief Convert a decimal string to a Decimal256 value, optionally including
213 /// precision and scale if they're passed in and not null.
214 static Status FromString(const util::string_view& s, Decimal256* out,
215 int32_t* precision, int32_t* scale = NULLPTR);
216 static Status FromString(const std::string& s, Decimal256* out, int32_t* precision,
217 int32_t* scale = NULLPTR);
218 static Status FromString(const char* s, Decimal256* out, int32_t* precision,
219 int32_t* scale = NULLPTR);
220 static Result<Decimal256> FromString(const util::string_view& s);
221 static Result<Decimal256> FromString(const std::string& s);
222 static Result<Decimal256> FromString(const char* s);
223
224 /// \brief Convert Decimal256 from one scale to another
225 Result<Decimal256> Rescale(int32_t original_scale, int32_t new_scale) const {
226 Decimal256 out;
227 auto dstatus = BasicDecimal256::Rescale(original_scale, new_scale, &out);
228 ARROW_RETURN_NOT_OK(ToArrowStatus(dstatus));
229 return std::move(out);
230 }
231
232 /// Divide this number by right and return the result.
233 ///
234 /// This operation is not destructive.
235 /// The answer rounds to zero. Signs work like:
236 /// 21 / 5 -> 4, 1
237 /// -21 / 5 -> -4, -1
238 /// 21 / -5 -> -4, 1
239 /// -21 / -5 -> 4, -1
240 /// \param[in] divisor the number to divide by
241 /// \return the pair of the quotient and the remainder
242 Result<std::pair<Decimal256, Decimal256>> Divide(const Decimal256& divisor) const {
243 std::pair<Decimal256, Decimal256> result;
244 auto dstatus = BasicDecimal256::Divide(divisor, &result.first, &result.second);
245 ARROW_RETURN_NOT_OK(ToArrowStatus(dstatus));
246 return std::move(result);
247 }
248
249 /// \brief Convert from a big-endian byte representation. The length must be
250 /// between 1 and 32.
251 /// \return error status if the length is an invalid value
252 static Result<Decimal256> FromBigEndian(const uint8_t* data, int32_t length);
253
254 static Result<Decimal256> FromReal(double real, int32_t precision, int32_t scale);
255 static Result<Decimal256> FromReal(float real, int32_t precision, int32_t scale);
256
257 /// \brief Convert to a floating-point number (scaled).
258 /// May return infinity in case of overflow.
259 float ToFloat(int32_t scale) const;
260 /// \brief Convert to a floating-point number (scaled)
261 double ToDouble(int32_t scale) const;
262
263 /// \brief Convert to a floating-point number (scaled)
264 template <typename T>
265 T ToReal(int32_t scale) const {
266 return ToRealConversion<T>::ToReal(*this, scale);
267 }
268
269 friend ARROW_EXPORT std::ostream& operator<<(std::ostream& os,
270 const Decimal256& decimal);
271
272 private:
273 /// Converts internal error code to Status
274 Status ToArrowStatus(DecimalStatus dstatus) const;
275
276 template <typename T>
277 struct ToRealConversion {};
278};
279
280template <>
281struct Decimal256::ToRealConversion<float> {
282 static float ToReal(const Decimal256& dec, int32_t scale) { return dec.ToFloat(scale); }
283};
284
285template <>
286struct Decimal256::ToRealConversion<double> {
287 static double ToReal(const Decimal256& dec, int32_t scale) {
288 return dec.ToDouble(scale);
289 }
290};
291
292/// For an integer type, return the max number of decimal digits
293/// (=minimal decimal precision) it can represent.
294inline Result<int32_t> MaxDecimalDigitsForInteger(Type::type type_id) {
295 switch (type_id) {
296 case Type::INT8:
297 case Type::UINT8:
298 return 3;
299 case Type::INT16:
300 case Type::UINT16:
301 return 5;
302 case Type::INT32:
303 case Type::UINT32:
304 return 10;
305 case Type::INT64:
306 case Type::UINT64:
307 return 19;
308 default:
309 break;
310 }
311 return Status::Invalid("Not an integer type: ", type_id);
312}
313
314} // namespace arrow