]>
Commit | Line | Data |
---|---|---|
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 | ||
32 | namespace 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. | |
51 | class 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 | ||
165 | template <> | |
166 | struct Decimal128::ToRealConversion<float> { | |
167 | static float ToReal(const Decimal128& dec, int32_t scale) { return dec.ToFloat(scale); } | |
168 | }; | |
169 | ||
170 | template <> | |
171 | struct 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. | |
188 | class 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 | ||
280 | template <> | |
281 | struct Decimal256::ToRealConversion<float> { | |
282 | static float ToReal(const Decimal256& dec, int32_t scale) { return dec.ToFloat(scale); } | |
283 | }; | |
284 | ||
285 | template <> | |
286 | struct 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. | |
294 | inline 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 |