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. Decimal128(const BasicDecimal128 & value)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. Decimal128()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 Divide(const Decimal128 & divisor)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 Rescale(int32_t original_scale,int32_t new_scale)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>> ToInteger()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>> ToInteger(T * out)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> ToReal(int32_t scale)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 315