yql_decimal.h 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. #pragma once
  2. #include <util/generic/strbuf.h>
  3. #include "yql_wide_int.h"
  4. #include <type_traits>
  5. #include <limits>
  6. namespace NYql {
  7. namespace NDecimal {
  8. #ifdef _win_
  9. #ifndef DONT_USE_NATIVE_INT128
  10. #define DONT_USE_NATIVE_INT128
  11. #endif
  12. #endif
  13. #ifdef DONT_USE_NATIVE_INT128
  14. using TInt128 = TWide<i64>;
  15. using TUint128 = TWide<ui64>;
  16. #else
  17. using TInt128 = signed __int128;
  18. using TUint128 = unsigned __int128;
  19. #endif
  20. template<ui8 Scale> struct TDivider;
  21. #if defined(__clang__) && defined(DONT_USE_NATIVE_INT128)
  22. template<> struct TDivider<0> { static inline constexpr TUint128 Value = 1U; };
  23. template<ui8 Scale> struct TDivider { static inline constexpr TInt128 Value = TDivider<Scale - 1U>::Value * 10U; };
  24. #else
  25. template<> struct TDivider<0> { static constexpr TUint128 Value = 1U; };
  26. template<ui8 Scale> struct TDivider { static constexpr TUint128 Value = TDivider<Scale - 1U>::Value * 10U; };
  27. #endif
  28. constexpr ui8 MaxPrecision = 35;
  29. static_assert(sizeof(TInt128) == 16, "Wrong size of TInt128, expected 16");
  30. inline constexpr TInt128 Inf() {
  31. return TInt128(100000000000000000ULL) * TInt128(1000000000000000000ULL);
  32. }
  33. inline constexpr TInt128 Nan() {
  34. return Inf() + TInt128(1);
  35. }
  36. inline constexpr TInt128 Err() {
  37. return Nan() + TInt128(1);
  38. }
  39. TUint128 GetDivider(ui8 scale);
  40. template<ui8 Precision>
  41. inline constexpr TUint128 GetDivider() {
  42. return TDivider<Precision>::Value;
  43. }
  44. template<ui8 Precision, bool IncLow = false, bool DecHigh = false>
  45. inline constexpr std::pair<TInt128, TInt128> GetBounds() {
  46. return std::make_pair(-GetDivider<Precision>() + (IncLow ? 1 : 0), +GetDivider<Precision>() - (DecHigh ? 1 : 0));
  47. }
  48. bool IsError(TInt128 v);
  49. bool IsNan(TInt128 v);
  50. bool IsInf(TInt128 v);
  51. bool IsNormal(TInt128 v);
  52. bool IsComparable(TInt128 v);
  53. template<ui8 Precision>
  54. inline bool IsNormal(TInt128 v) {
  55. const auto& b = GetBounds<Precision>();
  56. return v > b.first && v < b.second;
  57. }
  58. const char* ToString(TInt128 v, ui8 precision, ui8 scale = 0);
  59. TInt128 FromString(const TStringBuf& str, ui8 precision, ui8 scale = 0);
  60. // Accept string representation with exponent.
  61. TInt128 FromStringEx(const TStringBuf& str, ui8 precision, ui8 scale);
  62. template<typename TMkqlProto>
  63. inline TInt128 FromProto(const TMkqlProto& val) {
  64. ui64 half[2] = {val.GetLow128(), val.GetHi128()};
  65. TInt128 val128;
  66. std::memcpy(&val128, half, sizeof(val128));
  67. return val128;
  68. }
  69. template<typename TValue>
  70. inline constexpr TValue YtDecimalNan() {
  71. return std::numeric_limits<TValue>::max();
  72. }
  73. template<>
  74. inline constexpr TInt128 YtDecimalNan<TInt128>() {
  75. return ~(TInt128(1) << 127);
  76. }
  77. template<typename TValue>
  78. inline constexpr TValue YtDecimalInf() {
  79. return YtDecimalNan<TValue>() - 1;
  80. }
  81. template<typename TValue>
  82. inline TInt128 FromYtDecimal(TValue val) {
  83. static_assert(std::is_same<TInt128, TValue>::value || std::is_signed<TValue>::value, "Expected signed value");
  84. if (YtDecimalNan<TValue>() == val) {
  85. return Nan();
  86. } else if (YtDecimalInf<TValue>() == val) {
  87. return Inf();
  88. } else if (-YtDecimalInf<TValue>() == val) {
  89. return -Inf();
  90. } else {
  91. return TInt128(val);
  92. }
  93. }
  94. template<typename TValue>
  95. inline TValue ToYtDecimal(TInt128 val) {
  96. static_assert(std::is_same<TInt128, TValue>::value || std::is_signed<TValue>::value, "Expected signed value");
  97. if (IsNormal(val)) {
  98. return (TValue)val;
  99. } else if (val == Inf()) {
  100. return YtDecimalInf<TValue>();
  101. } else if (val == -Inf()) {
  102. return -YtDecimalInf<TValue>();
  103. }
  104. return YtDecimalNan<TValue>();
  105. }
  106. inline TInt128 FromHalfs(ui64 lo, i64 hi) {
  107. ui64 half[2] = {lo, static_cast<ui64>(hi)};
  108. TInt128 val128;
  109. std::memcpy(&val128, half, sizeof(val128));
  110. return val128;
  111. }
  112. inline std::pair<ui64, ui64> MakePair(const TInt128 v) {
  113. std::pair<ui64, ui64> r;
  114. std::memcpy(&r, &v, sizeof(v));
  115. return r;
  116. static_assert(sizeof(r) == sizeof(v), "Bad pair size.");
  117. }
  118. bool IsValid(const TStringBuf& str);
  119. // Round to nearest, ties to even.
  120. TInt128 Div(TInt128 a, TInt128 b); // a/b
  121. TInt128 Mul(TInt128 a, TInt128 b); // a*b
  122. TInt128 Mod(TInt128 a, TInt128 b); // a%b
  123. // a*b/c Only for non zero even normal positive divider.
  124. TInt128 MulAndDivNormalDivider(TInt128 a, TInt128 b, TInt128 c);
  125. // a*b/c Only for non zero normal positive multiplier.
  126. TInt128 MulAndDivNormalMultiplier(TInt128 a, TInt128 b, TInt128 c);
  127. struct TDecimal {
  128. TInt128 Value = 0;
  129. TDecimal() = default;
  130. template<typename T>
  131. TDecimal(T t): Value(t) { }
  132. explicit operator TInt128() const {
  133. return Value;
  134. }
  135. TDecimal& operator+=(TDecimal right) {
  136. const auto l = Value;
  137. const auto r = right.Value;
  138. const auto a = l + r;
  139. if (IsNormal(l) && IsNormal(r) && IsNormal(a)) {
  140. Value = a;
  141. } else if (IsNan(l) || IsNan(r) || !a /* inf - inf*/) {
  142. Value = Nan();
  143. } else {
  144. Value = a > 0
  145. ? +Inf()
  146. : -Inf();
  147. }
  148. return *this;
  149. }
  150. TDecimal& operator*=(TDecimal right) {
  151. Value = Mul(Value, right.Value);
  152. return *this;
  153. }
  154. TDecimal& operator/=(TDecimal right) {
  155. Value = Div(Value, right.Value);
  156. return *this;
  157. }
  158. friend TDecimal operator+(TDecimal left, TDecimal right) {
  159. left += right;
  160. return left;
  161. }
  162. friend TDecimal operator*(TDecimal left, TDecimal right) {
  163. left *= right;
  164. return left;
  165. }
  166. friend TDecimal operator/(TDecimal left, TDecimal right) {
  167. left /= right;
  168. return left;
  169. }
  170. };
  171. template<typename TRight>
  172. class TDecimalMultiplicator {
  173. protected:
  174. const TInt128 Bound;
  175. public:
  176. TDecimalMultiplicator(
  177. ui8 precision,
  178. ui8 scale = 0 /* unused */)
  179. : Bound(GetDivider(precision))
  180. {
  181. Y_UNUSED(scale);
  182. }
  183. TInt128 Do(TInt128 left, TRight right) const {
  184. TInt128 mul = Mul(left, right);
  185. if (mul > -Bound && mul < +Bound)
  186. return mul;
  187. return IsNan(mul) ? Nan() : (mul > 0 ? +Inf() : -Inf());
  188. }
  189. };
  190. template<>
  191. class TDecimalMultiplicator<TInt128> {
  192. protected:
  193. const TInt128 Bound;
  194. const TInt128 Divider;
  195. public:
  196. TDecimalMultiplicator(
  197. ui8 precision,
  198. ui8 scale)
  199. : Bound(GetDivider(precision))
  200. , Divider(GetDivider(scale))
  201. { }
  202. TInt128 Do(TInt128 left, TInt128 right) const {
  203. TInt128 mul = Divider > 1 ?
  204. MulAndDivNormalDivider(left, right, Divider):
  205. Mul(left, right);
  206. if (mul > -Bound && mul < +Bound)
  207. return mul;
  208. return IsNan(mul) ? Nan() : (mul > 0 ? +Inf() : -Inf());
  209. }
  210. };
  211. template<typename TRight>
  212. class TDecimalDivisor {
  213. public:
  214. TDecimalDivisor(
  215. ui8 precision = 0 /* unused */,
  216. ui8 scale = 0 /* unused */)
  217. {
  218. Y_UNUSED(precision);
  219. Y_UNUSED(scale);
  220. }
  221. TInt128 Do(TInt128 left, TRight right) const {
  222. return Div(left, right);
  223. }
  224. };
  225. template<>
  226. class TDecimalDivisor<TInt128> {
  227. protected:
  228. const TInt128 Bound;
  229. const TInt128 Divider;
  230. public:
  231. TDecimalDivisor(
  232. ui8 precision,
  233. ui8 scale)
  234. : Bound(GetDivider(precision))
  235. , Divider(GetDivider(scale))
  236. { }
  237. TInt128 Do(TInt128 left, TInt128 right) const {
  238. TInt128 div = MulAndDivNormalMultiplier(left, Divider, right);
  239. if (div > -Bound && div < +Bound) {
  240. return div;
  241. }
  242. return IsNan(div) ? Nan() : (div > 0 ? +Inf() : -Inf());
  243. }
  244. };
  245. template<typename TRight>
  246. class TDecimalRemainder {
  247. protected:
  248. const TInt128 Bound;
  249. const TInt128 Divider;
  250. public:
  251. TDecimalRemainder(
  252. ui8 precision,
  253. ui8 scale)
  254. : Bound(NYql::NDecimal::GetDivider(precision - scale))
  255. , Divider(NYql::NDecimal::GetDivider(scale))
  256. { }
  257. TInt128 Do(TInt128 left, TRight right) const {
  258. if constexpr (std::is_signed<TRight>::value) {
  259. if (TInt128(right) >= +Bound || TInt128(right) <= -Bound)
  260. return left;
  261. } else {
  262. if (TInt128(right) >= Bound)
  263. return left;
  264. }
  265. return Mod(left, Mul(Divider, right));
  266. }
  267. };
  268. template<>
  269. class TDecimalRemainder<TInt128> {
  270. public:
  271. TDecimalRemainder(
  272. ui8 precision = 0 /*unused*/,
  273. ui8 scale = 0 /*unused*/)
  274. {
  275. Y_UNUSED(precision);
  276. Y_UNUSED(scale);
  277. }
  278. TInt128 Do(TInt128 left, TInt128 right) const {
  279. return NYql::NDecimal::Mod(left, right);
  280. }
  281. };
  282. }
  283. }