]> git.proxmox.com Git - ceph.git/blame - ceph/src/arrow/cpp/src/arrow/util/basic_decimal.h
import quincy 17.2.0
[ceph.git] / ceph / src / arrow / cpp / src / arrow / util / basic_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 <array>
21#include <cstdint>
22#include <limits>
23#include <string>
24#include <type_traits>
25
26#include "arrow/util/endian.h"
27#include "arrow/util/macros.h"
28#include "arrow/util/type_traits.h"
29#include "arrow/util/visibility.h"
30
31namespace arrow {
32
33enum class DecimalStatus {
34 kSuccess,
35 kDivideByZero,
36 kOverflow,
37 kRescaleDataLoss,
38};
39
40/// Represents a signed 128-bit integer in two's complement.
41///
42/// This class is also compiled into LLVM IR - so, it should not have cpp references like
43/// streams and boost.
44class ARROW_EXPORT BasicDecimal128 {
45 struct LittleEndianArrayTag {};
46
47 public:
48 static constexpr int kBitWidth = 128;
49 static constexpr int kMaxPrecision = 38;
50 static constexpr int kMaxScale = 38;
51
52 // A constructor tag to introduce a little-endian encoded array
53 static constexpr LittleEndianArrayTag LittleEndianArray{};
54
55 /// \brief Create a BasicDecimal128 from the two's complement representation.
56#if ARROW_LITTLE_ENDIAN
57 constexpr BasicDecimal128(int64_t high, uint64_t low) noexcept
58 : low_bits_(low), high_bits_(high) {}
59#else
60 constexpr BasicDecimal128(int64_t high, uint64_t low) noexcept
61 : high_bits_(high), low_bits_(low) {}
62#endif
63
64 /// \brief Create a BasicDecimal256 from the two's complement representation.
65 ///
66 /// Input array is assumed to be in native endianness.
67#if ARROW_LITTLE_ENDIAN
68 constexpr BasicDecimal128(const std::array<uint64_t, 2>& array) noexcept
69 : low_bits_(array[0]), high_bits_(static_cast<int64_t>(array[1])) {}
70#else
71 constexpr BasicDecimal128(const std::array<uint64_t, 2>& array) noexcept
72 : high_bits_(static_cast<int64_t>(array[0])), low_bits_(array[1]) {}
73#endif
74
75 /// \brief Create a BasicDecimal128 from the two's complement representation.
76 ///
77 /// Input array is assumed to be in little endianness, with native endian elements.
78 BasicDecimal128(LittleEndianArrayTag, const std::array<uint64_t, 2>& array) noexcept
79 : BasicDecimal128(BitUtil::LittleEndianArray::ToNative(array)) {}
80
81 /// \brief Empty constructor creates a BasicDecimal128 with a value of 0.
82 constexpr BasicDecimal128() noexcept : BasicDecimal128(0, 0) {}
83
84 /// \brief Convert any integer value into a BasicDecimal128.
85 template <typename T,
86 typename = typename std::enable_if<
87 std::is_integral<T>::value && (sizeof(T) <= sizeof(uint64_t)), T>::type>
88 constexpr BasicDecimal128(T value) noexcept
89 : BasicDecimal128(value >= T{0} ? 0 : -1, static_cast<uint64_t>(value)) { // NOLINT
90 }
91
92 /// \brief Create a BasicDecimal128 from an array of bytes. Bytes are assumed to be in
93 /// native-endian byte order.
94 explicit BasicDecimal128(const uint8_t* bytes);
95
96 /// \brief Negate the current value (in-place)
97 BasicDecimal128& Negate();
98
99 /// \brief Absolute value (in-place)
100 BasicDecimal128& Abs();
101
102 /// \brief Absolute value
103 static BasicDecimal128 Abs(const BasicDecimal128& left);
104
105 /// \brief Add a number to this one. The result is truncated to 128 bits.
106 BasicDecimal128& operator+=(const BasicDecimal128& right);
107
108 /// \brief Subtract a number from this one. The result is truncated to 128 bits.
109 BasicDecimal128& operator-=(const BasicDecimal128& right);
110
111 /// \brief Multiply this number by another number. The result is truncated to 128 bits.
112 BasicDecimal128& operator*=(const BasicDecimal128& right);
113
114 /// Divide this number by right and return the result.
115 ///
116 /// This operation is not destructive.
117 /// The answer rounds to zero. Signs work like:
118 /// 21 / 5 -> 4, 1
119 /// -21 / 5 -> -4, -1
120 /// 21 / -5 -> -4, 1
121 /// -21 / -5 -> 4, -1
122 /// \param[in] divisor the number to divide by
123 /// \param[out] result the quotient
124 /// \param[out] remainder the remainder after the division
125 DecimalStatus Divide(const BasicDecimal128& divisor, BasicDecimal128* result,
126 BasicDecimal128* remainder) const;
127
128 /// \brief In-place division.
129 BasicDecimal128& operator/=(const BasicDecimal128& right);
130
131 /// \brief Bitwise "or" between two BasicDecimal128.
132 BasicDecimal128& operator|=(const BasicDecimal128& right);
133
134 /// \brief Bitwise "and" between two BasicDecimal128.
135 BasicDecimal128& operator&=(const BasicDecimal128& right);
136
137 /// \brief Shift left by the given number of bits.
138 BasicDecimal128& operator<<=(uint32_t bits);
139
140 /// \brief Shift right by the given number of bits. Negative values will
141 BasicDecimal128& operator>>=(uint32_t bits);
142
143 /// \brief Get the high bits of the two's complement representation of the number.
144 inline constexpr int64_t high_bits() const { return high_bits_; }
145
146 /// \brief Get the low bits of the two's complement representation of the number.
147 inline constexpr uint64_t low_bits() const { return low_bits_; }
148
149 /// \brief Get the bits of the two's complement representation of the number.
150 ///
151 /// The 2 elements are in native endian order. The bits within each uint64_t element
152 /// are in native endian order. For example, on a little endian machine,
153 /// BasicDecimal128(123).native_endian_array() = {123, 0};
154 /// but on a big endian machine,
155 /// BasicDecimal128(123).native_endian_array() = {0, 123};
156 inline std::array<uint64_t, 2> native_endian_array() const {
157#if ARROW_LITTLE_ENDIAN
158 return {low_bits_, static_cast<uint64_t>(high_bits_)};
159#else
160 return {static_cast<uint64_t>(high_bits_), low_bits_};
161#endif
162 }
163
164 /// \brief Get the bits of the two's complement representation of the number.
165 ///
166 /// The 2 elements are in little endian order. However, the bits within each
167 /// uint64_t element are in native endian order.
168 /// For example, BasicDecimal128(123).little_endian_array() = {123, 0};
169 inline std::array<uint64_t, 2> little_endian_array() const {
170 return {low_bits_, static_cast<uint64_t>(high_bits_)};
171 }
172
173 inline const uint8_t* native_endian_bytes() const {
174#if ARROW_LITTLE_ENDIAN
175 return reinterpret_cast<const uint8_t*>(&low_bits_);
176#else
177 return reinterpret_cast<const uint8_t*>(&high_bits_);
178#endif
179 }
180
181 inline uint8_t* mutable_native_endian_bytes() {
182#if ARROW_LITTLE_ENDIAN
183 return reinterpret_cast<uint8_t*>(&low_bits_);
184#else
185 return reinterpret_cast<uint8_t*>(&high_bits_);
186#endif
187 }
188
189 /// \brief Return the raw bytes of the value in native-endian byte order.
190 std::array<uint8_t, 16> ToBytes() const;
191 void ToBytes(uint8_t* out) const;
192
193 /// \brief separate the integer and fractional parts for the given scale.
194 void GetWholeAndFraction(int32_t scale, BasicDecimal128* whole,
195 BasicDecimal128* fraction) const;
196
197 /// \brief Scale multiplier for given scale value.
198 static const BasicDecimal128& GetScaleMultiplier(int32_t scale);
199 /// \brief Half-scale multiplier for given scale value.
200 static const BasicDecimal128& GetHalfScaleMultiplier(int32_t scale);
201
202 /// \brief Convert BasicDecimal128 from one scale to another
203 DecimalStatus Rescale(int32_t original_scale, int32_t new_scale,
204 BasicDecimal128* out) const;
205
206 /// \brief Scale up.
207 BasicDecimal128 IncreaseScaleBy(int32_t increase_by) const;
208
209 /// \brief Scale down.
210 /// - If 'round' is true, the right-most digits are dropped and the result value is
211 /// rounded up (+1 for +ve, -1 for -ve) based on the value of the dropped digits
212 /// (>= 10^reduce_by / 2).
213 /// - If 'round' is false, the right-most digits are simply dropped.
214 BasicDecimal128 ReduceScaleBy(int32_t reduce_by, bool round = true) const;
215
216 /// \brief Whether this number fits in the given precision
217 ///
218 /// Return true if the number of significant digits is less or equal to `precision`.
219 bool FitsInPrecision(int32_t precision) const;
220
221 // returns 1 for positive and zero decimal values, -1 for negative decimal values.
222 inline int64_t Sign() const { return 1 | (high_bits_ >> 63); }
223
224 /// \brief count the number of leading binary zeroes.
225 int32_t CountLeadingBinaryZeros() const;
226
227 /// \brief Get the maximum valid unscaled decimal value.
228 static const BasicDecimal128& GetMaxValue();
229
230 /// \brief Get the maximum decimal value (is not a valid value).
231 static inline constexpr BasicDecimal128 GetMaxSentinel() {
232 return BasicDecimal128(/*high=*/std::numeric_limits<int64_t>::max(),
233 /*low=*/std::numeric_limits<uint64_t>::max());
234 }
235 /// \brief Get the minimum decimal value (is not a valid value).
236 static inline constexpr BasicDecimal128 GetMinSentinel() {
237 return BasicDecimal128(/*high=*/std::numeric_limits<int64_t>::min(),
238 /*low=*/std::numeric_limits<uint64_t>::min());
239 }
240
241 private:
242#if ARROW_LITTLE_ENDIAN
243 uint64_t low_bits_;
244 int64_t high_bits_;
245#else
246 int64_t high_bits_;
247 uint64_t low_bits_;
248#endif
249};
250
251ARROW_EXPORT bool operator==(const BasicDecimal128& left, const BasicDecimal128& right);
252ARROW_EXPORT bool operator!=(const BasicDecimal128& left, const BasicDecimal128& right);
253ARROW_EXPORT bool operator<(const BasicDecimal128& left, const BasicDecimal128& right);
254ARROW_EXPORT bool operator<=(const BasicDecimal128& left, const BasicDecimal128& right);
255ARROW_EXPORT bool operator>(const BasicDecimal128& left, const BasicDecimal128& right);
256ARROW_EXPORT bool operator>=(const BasicDecimal128& left, const BasicDecimal128& right);
257
258ARROW_EXPORT BasicDecimal128 operator-(const BasicDecimal128& operand);
259ARROW_EXPORT BasicDecimal128 operator~(const BasicDecimal128& operand);
260ARROW_EXPORT BasicDecimal128 operator+(const BasicDecimal128& left,
261 const BasicDecimal128& right);
262ARROW_EXPORT BasicDecimal128 operator-(const BasicDecimal128& left,
263 const BasicDecimal128& right);
264ARROW_EXPORT BasicDecimal128 operator*(const BasicDecimal128& left,
265 const BasicDecimal128& right);
266ARROW_EXPORT BasicDecimal128 operator/(const BasicDecimal128& left,
267 const BasicDecimal128& right);
268ARROW_EXPORT BasicDecimal128 operator%(const BasicDecimal128& left,
269 const BasicDecimal128& right);
270
271class ARROW_EXPORT BasicDecimal256 {
272 private:
273 // Due to a bug in clang, we have to declare the extend method prior to its
274 // usage.
275 template <typename T>
276 inline static constexpr uint64_t extend(T low_bits) noexcept {
277 return low_bits >= T() ? uint64_t{0} : ~uint64_t{0};
278 }
279
280 struct LittleEndianArrayTag {};
281
282 public:
283 static constexpr int kBitWidth = 256;
284 static constexpr int kMaxPrecision = 76;
285 static constexpr int kMaxScale = 76;
286
287 // A constructor tag to denote a little-endian encoded array
288 static constexpr LittleEndianArrayTag LittleEndianArray{};
289
290 /// \brief Create a BasicDecimal256 from the two's complement representation.
291 ///
292 /// Input array is assumed to be in native endianness.
293 constexpr BasicDecimal256(const std::array<uint64_t, 4>& array) noexcept
294 : array_(array) {}
295
296 /// \brief Create a BasicDecimal256 from the two's complement representation.
297 ///
298 /// Input array is assumed to be in little endianness, with native endian elements.
299 BasicDecimal256(LittleEndianArrayTag, const std::array<uint64_t, 4>& array) noexcept
300 : BasicDecimal256(BitUtil::LittleEndianArray::ToNative(array)) {}
301
302 /// \brief Empty constructor creates a BasicDecimal256 with a value of 0.
303 constexpr BasicDecimal256() noexcept : array_({0, 0, 0, 0}) {}
304
305 /// \brief Convert any integer value into a BasicDecimal256.
306 template <typename T,
307 typename = typename std::enable_if<
308 std::is_integral<T>::value && (sizeof(T) <= sizeof(uint64_t)), T>::type>
309 constexpr BasicDecimal256(T value) noexcept
310 : array_(BitUtil::LittleEndianArray::ToNative<uint64_t, 4>(
311 {static_cast<uint64_t>(value), extend(value), extend(value),
312 extend(value)})) {}
313
314 explicit BasicDecimal256(const BasicDecimal128& value) noexcept
315 : array_(BitUtil::LittleEndianArray::ToNative<uint64_t, 4>(
316 {value.low_bits(), static_cast<uint64_t>(value.high_bits()),
317 extend(value.high_bits()), extend(value.high_bits())})) {}
318
319 /// \brief Create a BasicDecimal256 from an array of bytes. Bytes are assumed to be in
320 /// native-endian byte order.
321 explicit BasicDecimal256(const uint8_t* bytes);
322
323 /// \brief Negate the current value (in-place)
324 BasicDecimal256& Negate();
325
326 /// \brief Absolute value (in-place)
327 BasicDecimal256& Abs();
328
329 /// \brief Absolute value
330 static BasicDecimal256 Abs(const BasicDecimal256& left);
331
332 /// \brief Add a number to this one. The result is truncated to 256 bits.
333 BasicDecimal256& operator+=(const BasicDecimal256& right);
334
335 /// \brief Subtract a number from this one. The result is truncated to 256 bits.
336 BasicDecimal256& operator-=(const BasicDecimal256& right);
337
338 /// \brief Get the bits of the two's complement representation of the number.
339 ///
340 /// The 4 elements are in native endian order. The bits within each uint64_t element
341 /// are in native endian order. For example, on a little endian machine,
342 /// BasicDecimal256(123).native_endian_array() = {123, 0, 0, 0};
343 /// BasicDecimal256(-2).native_endian_array() = {0xFF...FE, 0xFF...FF, 0xFF...FF,
344 /// 0xFF...FF}.
345 /// while on a big endian machine,
346 /// BasicDecimal256(123).native_endian_array() = {0, 0, 0, 123};
347 /// BasicDecimal256(-2).native_endian_array() = {0xFF...FF, 0xFF...FF, 0xFF...FF,
348 /// 0xFF...FE}.
349 inline const std::array<uint64_t, 4>& native_endian_array() const { return array_; }
350
351 /// \brief Get the bits of the two's complement representation of the number.
352 ///
353 /// The 4 elements are in little endian order. However, the bits within each
354 /// uint64_t element are in native endian order.
355 /// For example, BasicDecimal256(123).little_endian_array() = {123, 0};
356 inline const std::array<uint64_t, 4> little_endian_array() const {
357 return BitUtil::LittleEndianArray::FromNative(array_);
358 }
359
360 inline const uint8_t* native_endian_bytes() const {
361 return reinterpret_cast<const uint8_t*>(array_.data());
362 }
363
364 inline uint8_t* mutable_native_endian_bytes() {
365 return reinterpret_cast<uint8_t*>(array_.data());
366 }
367
368 /// \brief Get the lowest bits of the two's complement representation of the number.
369 inline uint64_t low_bits() const { return BitUtil::LittleEndianArray::Make(array_)[0]; }
370
371 /// \brief Return the raw bytes of the value in native-endian byte order.
372 std::array<uint8_t, 32> ToBytes() const;
373 void ToBytes(uint8_t* out) const;
374
375 /// \brief Scale multiplier for given scale value.
376 static const BasicDecimal256& GetScaleMultiplier(int32_t scale);
377 /// \brief Half-scale multiplier for given scale value.
378 static const BasicDecimal256& GetHalfScaleMultiplier(int32_t scale);
379
380 /// \brief Convert BasicDecimal256 from one scale to another
381 DecimalStatus Rescale(int32_t original_scale, int32_t new_scale,
382 BasicDecimal256* out) const;
383
384 /// \brief Scale up.
385 BasicDecimal256 IncreaseScaleBy(int32_t increase_by) const;
386
387 /// \brief Scale down.
388 /// - If 'round' is true, the right-most digits are dropped and the result value is
389 /// rounded up (+1 for positive, -1 for negative) based on the value of the
390 /// dropped digits (>= 10^reduce_by / 2).
391 /// - If 'round' is false, the right-most digits are simply dropped.
392 BasicDecimal256 ReduceScaleBy(int32_t reduce_by, bool round = true) const;
393
394 /// \brief Whether this number fits in the given precision
395 ///
396 /// Return true if the number of significant digits is less or equal to `precision`.
397 bool FitsInPrecision(int32_t precision) const;
398
399 inline int64_t Sign() const {
400 return 1 | (static_cast<int64_t>(BitUtil::LittleEndianArray::Make(array_)[3]) >> 63);
401 }
402
403 inline int64_t IsNegative() const {
404 return static_cast<int64_t>(BitUtil::LittleEndianArray::Make(array_)[3]) < 0;
405 }
406
407 /// \brief Multiply this number by another number. The result is truncated to 256 bits.
408 BasicDecimal256& operator*=(const BasicDecimal256& right);
409
410 /// Divide this number by right and return the result.
411 ///
412 /// This operation is not destructive.
413 /// The answer rounds to zero. Signs work like:
414 /// 21 / 5 -> 4, 1
415 /// -21 / 5 -> -4, -1
416 /// 21 / -5 -> -4, 1
417 /// -21 / -5 -> 4, -1
418 /// \param[in] divisor the number to divide by
419 /// \param[out] result the quotient
420 /// \param[out] remainder the remainder after the division
421 DecimalStatus Divide(const BasicDecimal256& divisor, BasicDecimal256* result,
422 BasicDecimal256* remainder) const;
423
424 /// \brief Shift left by the given number of bits.
425 BasicDecimal256& operator<<=(uint32_t bits);
426
427 /// \brief In-place division.
428 BasicDecimal256& operator/=(const BasicDecimal256& right);
429
430 /// \brief Get the maximum decimal value (is not a valid value).
431 static inline constexpr BasicDecimal256 GetMaxSentinel() {
432#if ARROW_LITTLE_ENDIAN
433 return BasicDecimal256({std::numeric_limits<uint64_t>::max(),
434 std::numeric_limits<uint64_t>::max(),
435 std::numeric_limits<uint64_t>::max(),
436 static_cast<uint64_t>(std::numeric_limits<int64_t>::max())});
437#else
438 return BasicDecimal256({static_cast<uint64_t>(std::numeric_limits<int64_t>::max()),
439 std::numeric_limits<uint64_t>::max(),
440 std::numeric_limits<uint64_t>::max(),
441 std::numeric_limits<uint64_t>::max()});
442#endif
443 }
444 /// \brief Get the minimum decimal value (is not a valid value).
445 static inline constexpr BasicDecimal256 GetMinSentinel() {
446#if ARROW_LITTLE_ENDIAN
447 return BasicDecimal256(
448 {0, 0, 0, static_cast<uint64_t>(std::numeric_limits<int64_t>::min())});
449#else
450 return BasicDecimal256(
451 {static_cast<uint64_t>(std::numeric_limits<int64_t>::min()), 0, 0, 0});
452#endif
453 }
454
455 private:
456 std::array<uint64_t, 4> array_;
457};
458
459ARROW_EXPORT inline bool operator==(const BasicDecimal256& left,
460 const BasicDecimal256& right) {
461 return left.native_endian_array() == right.native_endian_array();
462}
463
464ARROW_EXPORT inline bool operator!=(const BasicDecimal256& left,
465 const BasicDecimal256& right) {
466 return left.native_endian_array() != right.native_endian_array();
467}
468
469ARROW_EXPORT bool operator<(const BasicDecimal256& left, const BasicDecimal256& right);
470
471ARROW_EXPORT inline bool operator<=(const BasicDecimal256& left,
472 const BasicDecimal256& right) {
473 return !operator<(right, left);
474}
475
476ARROW_EXPORT inline bool operator>(const BasicDecimal256& left,
477 const BasicDecimal256& right) {
478 return operator<(right, left);
479}
480
481ARROW_EXPORT inline bool operator>=(const BasicDecimal256& left,
482 const BasicDecimal256& right) {
483 return !operator<(left, right);
484}
485
486ARROW_EXPORT BasicDecimal256 operator-(const BasicDecimal256& operand);
487ARROW_EXPORT BasicDecimal256 operator~(const BasicDecimal256& operand);
488ARROW_EXPORT BasicDecimal256 operator+(const BasicDecimal256& left,
489 const BasicDecimal256& right);
490ARROW_EXPORT BasicDecimal256 operator*(const BasicDecimal256& left,
491 const BasicDecimal256& right);
492ARROW_EXPORT BasicDecimal256 operator/(const BasicDecimal256& left,
493 const BasicDecimal256& right);
494} // namespace arrow