Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-08-28 08:27:08

0001 // Licensed to the Apache Software Foundation (ASF) under one
0002 // or more contributor license agreements.  See the NOTICE file
0003 // distributed with this work for additional information
0004 // regarding copyright ownership.  The ASF licenses this file
0005 // to you under the Apache License, Version 2.0 (the
0006 // "License"); you may not use this file except in compliance
0007 // with the License.  You may obtain a copy of the License at
0008 //
0009 //   http://www.apache.org/licenses/LICENSE-2.0
0010 //
0011 // Unless required by applicable law or agreed to in writing,
0012 // software distributed under the License is distributed on an
0013 // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
0014 // KIND, either express or implied.  See the License for the
0015 // specific language governing permissions and limitations
0016 // under the License.
0017 
0018 #pragma once
0019 
0020 #include <cstdint>
0021 #include <iosfwd>
0022 #include <limits>
0023 #include <string>
0024 #include <string_view>
0025 #include <utility>
0026 
0027 #include "arrow/result.h"
0028 #include "arrow/status.h"
0029 #include "arrow/type_fwd.h"
0030 #include "arrow/util/basic_decimal.h"
0031 
0032 namespace arrow {
0033 
0034 class Decimal64;
0035 
0036 /// Represents a signed 32-bit decimal value in two's complement.
0037 /// Calulations wrap around and overflow is ignored.
0038 /// The max decimal precision that can be safely represented is
0039 /// 9 significant digits.
0040 ///
0041 /// The implementation is split into two parts :
0042 ///
0043 /// 1. BasicDecimal32
0044 ///    - can be safely compiled to IR without references to libstdc++
0045 /// 2. Decimal32
0046 ///    - has additional functionality on top of BasicDecimal32 to deal with
0047 ///      strings and streams
0048 class ARROW_EXPORT Decimal32 : public BasicDecimal32 {
0049  public:
0050   /// \cond FALSE
0051   // (need to avoid a duplicate definition in sphinx)
0052   using BasicDecimal32::BasicDecimal32;
0053   /// \endcond
0054 
0055   /// \brief constructor creates a Decimal32 from a BasicDecimal32
0056   constexpr Decimal32(const BasicDecimal32& value) noexcept  // NOLINT runtime/explicit
0057       : BasicDecimal32(value) {}
0058 
0059   /// \brief Parse the number from a base 10 string representation
0060   explicit Decimal32(const std::string& value);
0061 
0062   /// \brief Empty constructor creates a Decimal32 with a value of 0
0063   /// this is required for some older compilers
0064   constexpr Decimal32() noexcept : BasicDecimal32() {}
0065 
0066   /// \brief Divide this number by right and return the result.
0067   ///
0068   /// This operation is not destructive.
0069   /// The answer rounds to zero. Signs work like:
0070   ///   21 /  5 ->  4,  1
0071   ///  -21 /  5 -> -4, -1
0072   ///   21 / -5 -> -4,  1
0073   ///  -21 / -5 ->  4, -1
0074   /// \param[in] divisor the number to divide by
0075   /// \return the pair of the quotient and the remainder
0076   Result<std::pair<Decimal32, Decimal32>> Divide(const Decimal32& divisor) const {
0077     std::pair<Decimal32, Decimal32> result;
0078     auto dstatus = BasicDecimal32::Divide(divisor, &result.first, &result.second);
0079     ARROW_RETURN_NOT_OK(ToArrowStatus(dstatus));
0080     return result;
0081   }
0082 
0083   /// \brief Convert the Decimal32 value to a base 10 decimal string with the given scale
0084   std::string ToString(int32_t scale) const;
0085 
0086   /// \brief Convert the value to an integer string
0087   std::string ToIntegerString() const;
0088 
0089   /// \brief Cast this value to an int64_t
0090   explicit operator int64_t() const;
0091 
0092   explicit operator Decimal64() const;
0093 
0094   /// \brief Convert a decimal string to a Decimal value, optionally including
0095   /// precision and scale if they're passed in and not null.
0096   static Status FromString(std::string_view s, Decimal32* out, int32_t* precision,
0097                            int32_t* scale = NULLPTR);
0098   static Status FromString(const std::string& s, Decimal32* out, int32_t* precision,
0099                            int32_t* scale = NULLPTR);
0100   static Status FromString(const char* s, Decimal32* out, int32_t* precision,
0101                            int32_t* scale = NULLPTR);
0102   static Result<Decimal32> FromString(std::string_view s);
0103   static Result<Decimal32> FromString(const std::string& s);
0104   static Result<Decimal32> FromString(const char* s);
0105 
0106   static Result<Decimal32> FromReal(double real, int32_t precision, int32_t scale);
0107   static Result<Decimal32> FromReal(float real, int32_t precision, int32_t scale);
0108 
0109   /// \brief Convert from a big-endian byte representation. The length must be
0110   ///        between 1 and 4
0111   /// \return error statis if the length is an invalid value
0112   static Result<Decimal32> FromBigEndian(const uint8_t* data, int32_t length);
0113 
0114   /// \brief Convert Decimal32 from one scale to another
0115   Result<Decimal32> Rescale(int32_t original_scale, int32_t new_scale) const {
0116     Decimal32 out;
0117     auto dstatus = BasicDecimal32::Rescale(original_scale, new_scale, &out);
0118     ARROW_RETURN_NOT_OK(ToArrowStatus(dstatus));
0119     return out;
0120   }
0121 
0122   /// \brief Convert to a signed integer
0123   template <typename T, typename = internal::EnableIfIsOneOf<T, int32_t, int64_t>>
0124   Result<T> ToInteger() const {
0125     return static_cast<T>(value_);
0126   }
0127 
0128   /// \brief Convert to a signed integer
0129   template <typename T, typename = internal::EnableIfIsOneOf<T, int32_t, int64_t>>
0130   Status ToInteger(T* out) const {
0131     return ToInteger<T>().Value(out);
0132   }
0133 
0134   /// \brief Convert to a floating-point number (scaled)
0135   float ToFloat(int32_t scale) const;
0136   /// \brief Convert to a floating-point number (scaled)
0137   double ToDouble(int32_t scale) const;
0138 
0139   /// \brief Convert to a floating-point number (scaled)
0140   template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
0141   T ToReal(int32_t scale) const {
0142     static_assert(std::is_same_v<T, float> || std::is_same_v<T, double>,
0143                   "Unexpected floating-point type");
0144     if constexpr (std::is_same_v<T, float>) {
0145       return ToFloat(scale);
0146     } else {
0147       return ToDouble(scale);
0148     }
0149   }
0150 
0151   ARROW_FRIEND_EXPORT friend std::ostream& operator<<(std::ostream& os,
0152                                                       const Decimal32& decimal);
0153 
0154  private:
0155   /// Converts internal error code to Status
0156   Status ToArrowStatus(DecimalStatus dstatus) const;
0157 };
0158 
0159 class ARROW_EXPORT Decimal64 : public BasicDecimal64 {
0160  public:
0161   /// \cond FALSE
0162   // (need to avoid a duplicate definition in sphinx)
0163   using BasicDecimal64::BasicDecimal64;
0164   /// \endcond
0165 
0166   /// \brief constructor creates a Decimal64 from a BasicDecimal64
0167   constexpr Decimal64(const BasicDecimal64& value) noexcept  // NOLINT runtime/explicit
0168       : BasicDecimal64(value) {}
0169 
0170   explicit Decimal64(const BasicDecimal32& value) noexcept
0171       : BasicDecimal64(static_cast<int64_t>(value.value())) {}
0172 
0173   /// \brief Parse the number from a base 10 string representation
0174   explicit Decimal64(const std::string& value);
0175 
0176   /// \brief Empty constructor creates a Decimal64 with a value of 0
0177   /// this is required for some older compilers
0178   constexpr Decimal64() noexcept : BasicDecimal64() {}
0179 
0180   /// \brief Divide this number by right and return the result.
0181   ///
0182   /// This operation is not destructive.
0183   /// The answer rounds to zero. Signs work like:
0184   ///   21 /  5 ->  4,  1
0185   ///  -21 /  5 -> -4, -1
0186   ///   21 / -5 -> -4,  1
0187   ///  -21 / -5 ->  4, -1
0188   /// \param[in] divisor the number to divide by
0189   /// \return the pair of the quotient and the remainder
0190   Result<std::pair<Decimal64, Decimal64>> Divide(const Decimal64& divisor) const {
0191     std::pair<Decimal64, Decimal64> result;
0192     auto dstatus = BasicDecimal64::Divide(divisor, &result.first, &result.second);
0193     ARROW_RETURN_NOT_OK(ToArrowStatus(dstatus));
0194     return result;
0195   }
0196 
0197   /// \brief Convert the Decimal64 value to a base 10 decimal string with the given scale
0198   std::string ToString(int32_t scale) const;
0199 
0200   /// \brief Convert the value to an integer string
0201   std::string ToIntegerString() const;
0202 
0203   /// \brief Cast this value to an int64_t
0204   explicit operator int64_t() const;
0205 
0206   /// \brief Convert a decimal string to a Decimal value, optionally including
0207   /// precision and scale if they're passed in and not null.
0208   static Status FromString(std::string_view s, Decimal64* out, int32_t* precision,
0209                            int32_t* scale = NULLPTR);
0210   static Status FromString(const std::string& s, Decimal64* out, int32_t* precision,
0211                            int32_t* scale = NULLPTR);
0212   static Status FromString(const char* s, Decimal64* out, int32_t* precision,
0213                            int32_t* scale = NULLPTR);
0214   static Result<Decimal64> FromString(std::string_view s);
0215   static Result<Decimal64> FromString(const std::string& s);
0216   static Result<Decimal64> FromString(const char* s);
0217 
0218   static Result<Decimal64> FromReal(double real, int32_t precision, int32_t scale);
0219   static Result<Decimal64> FromReal(float real, int32_t precision, int32_t scale);
0220 
0221   /// \brief Convert from a big-endian byte representation. The length must be
0222   ///        between 1 and 4
0223   /// \return error statis if the length is an invalid value
0224   static Result<Decimal64> FromBigEndian(const uint8_t* data, int32_t length);
0225 
0226   /// \brief Convert Decimal64 from one scale to another
0227   Result<Decimal64> Rescale(int32_t original_scale, int32_t new_scale) const {
0228     Decimal64 out;
0229     auto dstatus = BasicDecimal64::Rescale(original_scale, new_scale, &out);
0230     ARROW_RETURN_NOT_OK(ToArrowStatus(dstatus));
0231     return out;
0232   }
0233 
0234   /// \brief Convert to a signed integer
0235   template <typename T, typename = internal::EnableIfIsOneOf<T, int32_t, int64_t>>
0236   Result<T> ToInteger() const {
0237     return static_cast<T>(value_);
0238   }
0239 
0240   /// \brief Convert to a signed integer
0241   template <typename T, typename = internal::EnableIfIsOneOf<T, int32_t, int64_t>>
0242   Status ToInteger(T* out) const {
0243     return ToInteger<T>().Value(out);
0244   }
0245 
0246   /// \brief Convert to a floating-point number (scaled)
0247   float ToFloat(int32_t scale) const;
0248   /// \brief Convert to a floating-point number (scaled)
0249   double ToDouble(int32_t scale) const;
0250 
0251   /// \brief Convert to a floating-point number (scaled)
0252   template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
0253   T ToReal(int32_t scale) const {
0254     static_assert(std::is_same_v<T, float> || std::is_same_v<T, double>,
0255                   "Unexpected floating-point type");
0256     if constexpr (std::is_same_v<T, float>) {
0257       return ToFloat(scale);
0258     } else {
0259       return ToDouble(scale);
0260     }
0261   }
0262 
0263   ARROW_FRIEND_EXPORT friend std::ostream& operator<<(std::ostream& os,
0264                                                       const Decimal64& decimal);
0265 
0266  private:
0267   /// Converts internal error code to Status
0268   Status ToArrowStatus(DecimalStatus dstatus) const;
0269 };
0270 
0271 /// Represents a signed 128-bit integer in two's complement.
0272 /// Calculations wrap around and overflow is ignored.
0273 /// The max decimal precision that can be safely represented is
0274 /// 38 significant digits.
0275 ///
0276 /// For a discussion of the algorithms, look at Knuth's volume 2,
0277 /// Semi-numerical Algorithms section 4.3.1.
0278 ///
0279 /// Adapted from the Apache ORC C++ implementation
0280 ///
0281 /// The implementation is split into two parts :
0282 ///
0283 /// 1. BasicDecimal128
0284 ///    - can be safely compiled to IR without references to libstdc++.
0285 /// 2. Decimal128
0286 ///    - has additional functionality on top of BasicDecimal128 to deal with
0287 ///      strings and streams.
0288 class ARROW_EXPORT Decimal128 : public BasicDecimal128 {
0289  public:
0290   /// \cond FALSE
0291   // (need to avoid a duplicate definition in Sphinx)
0292   using BasicDecimal128::BasicDecimal128;
0293   /// \endcond
0294 
0295   /// \brief constructor creates a Decimal128 from a BasicDecimal128.
0296   constexpr Decimal128(const BasicDecimal128& value) noexcept  // NOLINT runtime/explicit
0297       : BasicDecimal128(value) {}
0298 
0299   /// \brief Parse the number from a base 10 string representation.
0300   explicit Decimal128(const std::string& value);
0301 
0302   /// \brief Empty constructor creates a Decimal128 with a value of 0.
0303   // This is required on some older compilers.
0304   constexpr Decimal128() noexcept : BasicDecimal128() {}
0305 
0306   /// Divide this number by right and return the result.
0307   ///
0308   /// This operation is not destructive.
0309   /// The answer rounds to zero. Signs work like:
0310   ///   21 /  5 ->  4,  1
0311   ///  -21 /  5 -> -4, -1
0312   ///   21 / -5 -> -4,  1
0313   ///  -21 / -5 ->  4, -1
0314   /// \param[in] divisor the number to divide by
0315   /// \return the pair of the quotient and the remainder
0316   Result<std::pair<Decimal128, Decimal128>> Divide(const Decimal128& divisor) const {
0317     std::pair<Decimal128, Decimal128> result;
0318     auto dstatus = BasicDecimal128::Divide(divisor, &result.first, &result.second);
0319     ARROW_RETURN_NOT_OK(ToArrowStatus(dstatus));
0320     return result;
0321   }
0322 
0323   /// \brief Convert the Decimal128 value to a base 10 decimal string with the given
0324   /// scale.
0325   std::string ToString(int32_t scale) const;
0326 
0327   /// \brief Convert the value to an integer string
0328   std::string ToIntegerString() const;
0329 
0330   /// \brief Cast this value to an int64_t.
0331   explicit operator int64_t() const;
0332 
0333   /// \brief Convert a decimal string to a Decimal128 value, optionally including
0334   /// precision and scale if they're passed in and not null.
0335   static Status FromString(std::string_view s, Decimal128* out, int32_t* precision,
0336                            int32_t* scale = NULLPTR);
0337   static Status FromString(const std::string& s, Decimal128* out, int32_t* precision,
0338                            int32_t* scale = NULLPTR);
0339   static Status FromString(const char* s, Decimal128* out, int32_t* precision,
0340                            int32_t* scale = NULLPTR);
0341   static Result<Decimal128> FromString(std::string_view s);
0342   static Result<Decimal128> FromString(const std::string& s);
0343   static Result<Decimal128> FromString(const char* s);
0344 
0345   static Result<Decimal128> FromReal(double real, int32_t precision, int32_t scale);
0346   static Result<Decimal128> FromReal(float real, int32_t precision, int32_t scale);
0347 
0348   /// \brief Convert from a big-endian byte representation. The length must be
0349   ///        between 1 and 16.
0350   /// \return error status if the length is an invalid value
0351   static Result<Decimal128> FromBigEndian(const uint8_t* data, int32_t length);
0352 
0353   /// \brief Convert Decimal128 from one scale to another
0354   Result<Decimal128> Rescale(int32_t original_scale, int32_t new_scale) const {
0355     Decimal128 out;
0356     auto dstatus = BasicDecimal128::Rescale(original_scale, new_scale, &out);
0357     ARROW_RETURN_NOT_OK(ToArrowStatus(dstatus));
0358     return out;
0359   }
0360 
0361   /// \brief Convert to a signed integer
0362   template <typename T, typename = internal::EnableIfIsOneOf<T, int32_t, int64_t>>
0363   Result<T> ToInteger() const {
0364     constexpr auto min_value = std::numeric_limits<T>::min();
0365     constexpr auto max_value = std::numeric_limits<T>::max();
0366     const auto& self = *this;
0367     if (self < min_value || self > max_value) {
0368       return Status::Invalid("Invalid cast from Decimal128 to ", sizeof(T),
0369                              " byte integer");
0370     }
0371     return static_cast<T>(low_bits());
0372   }
0373 
0374   /// \brief Convert to a signed integer
0375   template <typename T, typename = internal::EnableIfIsOneOf<T, int32_t, int64_t>>
0376   Status ToInteger(T* out) const {
0377     return ToInteger<T>().Value(out);
0378   }
0379 
0380   /// \brief Convert to a floating-point number (scaled)
0381   float ToFloat(int32_t scale) const;
0382   /// \brief Convert to a floating-point number (scaled)
0383   double ToDouble(int32_t scale) const;
0384 
0385   /// \brief Convert to a floating-point number (scaled)
0386   template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
0387   T ToReal(int32_t scale) const {
0388     static_assert(std::is_same_v<T, float> || std::is_same_v<T, double>,
0389                   "Unexpected floating-point type");
0390     if constexpr (std::is_same_v<T, float>) {
0391       return ToFloat(scale);
0392     } else {
0393       return ToDouble(scale);
0394     }
0395   }
0396 
0397   ARROW_FRIEND_EXPORT friend std::ostream& operator<<(std::ostream& os,
0398                                                       const Decimal128& decimal);
0399 
0400  private:
0401   /// Converts internal error code to Status
0402   Status ToArrowStatus(DecimalStatus dstatus) const;
0403 };
0404 
0405 /// Represents a signed 256-bit integer in two's complement.
0406 /// The max decimal precision that can be safely represented is
0407 /// 76 significant digits.
0408 ///
0409 /// The implementation is split into two parts :
0410 ///
0411 /// 1. BasicDecimal256
0412 ///    - can be safely compiled to IR without references to libstdc++.
0413 /// 2. Decimal256
0414 ///    - (TODO) has additional functionality on top of BasicDecimal256 to deal with
0415 ///      strings and streams.
0416 class ARROW_EXPORT Decimal256 : public BasicDecimal256 {
0417  public:
0418   /// \cond FALSE
0419   // (need to avoid a duplicate definition in Sphinx)
0420   using BasicDecimal256::BasicDecimal256;
0421   /// \endcond
0422 
0423   /// \brief constructor creates a Decimal256 from a BasicDecimal256.
0424   constexpr Decimal256(const BasicDecimal256& value) noexcept  // NOLINT(runtime/explicit)
0425       : BasicDecimal256(value) {}
0426 
0427   /// \brief Parse the number from a base 10 string representation.
0428   explicit Decimal256(const std::string& value);
0429 
0430   /// \brief Empty constructor creates a Decimal256 with a value of 0.
0431   // This is required on some older compilers.
0432   constexpr Decimal256() noexcept : BasicDecimal256() {}
0433 
0434   /// \brief Convert the Decimal256 value to a base 10 decimal string with the given
0435   /// scale.
0436   std::string ToString(int32_t scale) const;
0437 
0438   /// \brief Convert the value to an integer string
0439   std::string ToIntegerString() const;
0440 
0441   /// \brief Convert a decimal string to a Decimal256 value, optionally including
0442   /// precision and scale if they're passed in and not null.
0443   static Status FromString(std::string_view s, Decimal256* out, int32_t* precision,
0444                            int32_t* scale = NULLPTR);
0445   static Status FromString(const std::string& s, Decimal256* out, int32_t* precision,
0446                            int32_t* scale = NULLPTR);
0447   static Status FromString(const char* s, Decimal256* out, int32_t* precision,
0448                            int32_t* scale = NULLPTR);
0449   static Result<Decimal256> FromString(std::string_view s);
0450   static Result<Decimal256> FromString(const std::string& s);
0451   static Result<Decimal256> FromString(const char* s);
0452 
0453   /// \brief Convert Decimal256 from one scale to another
0454   Result<Decimal256> Rescale(int32_t original_scale, int32_t new_scale) const {
0455     Decimal256 out;
0456     auto dstatus = BasicDecimal256::Rescale(original_scale, new_scale, &out);
0457     ARROW_RETURN_NOT_OK(ToArrowStatus(dstatus));
0458     return out;
0459   }
0460 
0461   /// Divide this number by right and return the result.
0462   ///
0463   /// This operation is not destructive.
0464   /// The answer rounds to zero. Signs work like:
0465   ///   21 /  5 ->  4,  1
0466   ///  -21 /  5 -> -4, -1
0467   ///   21 / -5 -> -4,  1
0468   ///  -21 / -5 ->  4, -1
0469   /// \param[in] divisor the number to divide by
0470   /// \return the pair of the quotient and the remainder
0471   Result<std::pair<Decimal256, Decimal256>> Divide(const Decimal256& divisor) const {
0472     std::pair<Decimal256, Decimal256> result;
0473     auto dstatus = BasicDecimal256::Divide(divisor, &result.first, &result.second);
0474     ARROW_RETURN_NOT_OK(ToArrowStatus(dstatus));
0475     return result;
0476   }
0477 
0478   /// \brief Convert from a big-endian byte representation. The length must be
0479   ///        between 1 and 32.
0480   /// \return error status if the length is an invalid value
0481   static Result<Decimal256> FromBigEndian(const uint8_t* data, int32_t length);
0482 
0483   static Result<Decimal256> FromReal(double real, int32_t precision, int32_t scale);
0484   static Result<Decimal256> FromReal(float real, int32_t precision, int32_t scale);
0485 
0486   /// \brief Convert to a floating-point number (scaled).
0487   /// May return infinity in case of overflow.
0488   float ToFloat(int32_t scale) const;
0489   /// \brief Convert to a floating-point number (scaled)
0490   double ToDouble(int32_t scale) const;
0491 
0492   /// \brief Convert to a floating-point number (scaled)
0493   template <typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
0494   T ToReal(int32_t scale) const {
0495     static_assert(std::is_same_v<T, float> || std::is_same_v<T, double>,
0496                   "Unexpected floating-point type");
0497     if constexpr (std::is_same_v<T, float>) {
0498       return ToFloat(scale);
0499     } else {
0500       return ToDouble(scale);
0501     }
0502   }
0503 
0504   ARROW_FRIEND_EXPORT friend std::ostream& operator<<(std::ostream& os,
0505                                                       const Decimal256& decimal);
0506 
0507  private:
0508   /// Converts internal error code to Status
0509   Status ToArrowStatus(DecimalStatus dstatus) const;
0510 };
0511 
0512 /// For an integer type, return the max number of decimal digits
0513 /// (=minimal decimal precision) it can represent.
0514 inline Result<int32_t> MaxDecimalDigitsForInteger(Type::type type_id) {
0515   switch (type_id) {
0516     case Type::INT8:
0517     case Type::UINT8:
0518       return 3;
0519     case Type::INT16:
0520     case Type::UINT16:
0521       return 5;
0522     case Type::INT32:
0523     case Type::UINT32:
0524       return 10;
0525     case Type::INT64:
0526       return 19;
0527     case Type::UINT64:
0528       return 20;
0529     default:
0530       break;
0531   }
0532   return Status::Invalid("Not an integer type: ", type_id);
0533 }
0534 
0535 }  // namespace arrow