yql_decimal.cpp 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  1. #include "yql_decimal.h"
  2. #include <cstring>
  3. #include <ostream>
  4. #include <string>
  5. #include <charconv>
  6. namespace NYql {
  7. namespace NDecimal {
  8. static const TUint128 Ten(10U);
  9. TUint128 GetDivider(ui8 scale) {
  10. if (scale > MaxPrecision) {
  11. return Inf();
  12. }
  13. TUint128 d(1U);
  14. while (scale--)
  15. d *= Ten;
  16. return d;
  17. }
  18. bool IsError(TInt128 v) {
  19. return v > Nan() || v < -Inf();
  20. }
  21. bool IsNan(TInt128 v) {
  22. return v == Nan();
  23. }
  24. bool IsInf(TInt128 v) {
  25. return v == Inf() || v == -Inf();
  26. }
  27. bool IsNormal(TInt128 v) {
  28. return v < Inf() && v > -Inf();
  29. }
  30. bool IsComparable(TInt128 v) {
  31. return v <= Inf() && v >= -Inf();
  32. }
  33. const char* ToString(TInt128 val, ui8 precision, ui8 scale) {
  34. if (!precision || precision > MaxPrecision || scale > precision) {
  35. return nullptr;
  36. }
  37. if (val == Inf())
  38. return "inf";
  39. if (val == -Inf())
  40. return "-inf";
  41. if (val == Nan())
  42. return "nan";
  43. if (!IsNormal(val)) {
  44. return nullptr;
  45. }
  46. if (!val) {
  47. return "0";
  48. }
  49. const bool neg = val < 0;
  50. TUint128 v = neg ? -val : val;
  51. // log_{10}(2^120) ~= 36.12, 37 decimal places
  52. // plus dot, zero before dot, sign and zero byte at the end
  53. static thread_local char str[40];
  54. auto end = str + sizeof(str);
  55. *--end = 0;
  56. auto s = end;
  57. do {
  58. if (!precision--) {
  59. return nullptr;
  60. }
  61. const auto digit = ui8(v % Ten);
  62. if (digit || !scale || s != end) {
  63. *--s = "0123456789"[digit];
  64. }
  65. if (scale && !--scale && s != end) {
  66. *--s = '.';
  67. }
  68. } while (v /= Ten);
  69. if (scale) {
  70. do {
  71. if (!precision--) {
  72. return nullptr;
  73. }
  74. *--s = '0';
  75. } while (--scale);
  76. *--s = '.';
  77. }
  78. if (*s == '.') {
  79. *--s = '0';
  80. }
  81. if (neg) {
  82. *--s = '-';
  83. }
  84. return s;
  85. }
  86. namespace {
  87. bool IsNan(const char* s) {
  88. return (s[0] == 'N' || s[0] == 'n') && (s[1] == 'A' || s[1] == 'a') && (s[2] == 'N' || s[2] == 'n');
  89. }
  90. bool IsInf(const char* s) {
  91. return (s[0] == 'I' || s[0] == 'i') && (s[1] == 'N' || s[1] == 'n') && (s[2] == 'F' || s[2] == 'f');
  92. }
  93. }
  94. TInt128 FromString(const TStringBuf& str, ui8 precision, ui8 scale) {
  95. if (scale > precision)
  96. return Err();
  97. auto s = str.data();
  98. auto l = str.size();
  99. if (!s || !l)
  100. return Err();
  101. const bool neg = '-' == *s;
  102. if (neg || '+' == *s) {
  103. ++s;
  104. --l;
  105. }
  106. if (3U == l) {
  107. if (IsInf(s))
  108. return neg ? -Inf() : Inf();
  109. if (IsNan(s))
  110. return Nan();
  111. }
  112. TUint128 v = 0U;
  113. auto integral = precision - scale;
  114. for (bool dot = false; l; --l) {
  115. if (*s == '.') {
  116. if (dot)
  117. return Err();
  118. ++s;
  119. dot = true;
  120. continue;
  121. }
  122. if (dot) {
  123. if (scale)
  124. --scale;
  125. else
  126. break;
  127. }
  128. const char c = *s++;
  129. if (!std::isdigit(c))
  130. return Err();
  131. v *= Ten;
  132. v += c - '0';
  133. if (!dot && v && !integral--) {
  134. return neg ? -Inf() : Inf();
  135. }
  136. }
  137. if (l--) {
  138. const char c = *s++;
  139. if (!std::isdigit(c))
  140. return Err();
  141. bool plus = c > '5';
  142. if (!plus && c == '5') {
  143. for (plus = v & 1; !plus && l; --l) {
  144. const char c = *s++;
  145. if (!std::isdigit(c))
  146. return Err();
  147. plus = c != '0';
  148. }
  149. }
  150. while (l--)
  151. if (!std::isdigit(*s++))
  152. return Err();
  153. if (plus)
  154. if (++v >= GetDivider(precision))
  155. v = Inf();
  156. }
  157. while (scale--)
  158. v *= Ten;
  159. return neg ? -v : v;
  160. }
  161. TInt128 FromStringEx(const TStringBuf& str, ui8 precision, ui8 scale) {
  162. if (scale > precision)
  163. return Err();
  164. const auto s = str.data();
  165. for (auto ptr = s + str.size() - 1U; ptr > s; --ptr) {
  166. if (*ptr == 'E' || *ptr == 'e') {
  167. const auto len = ptr - s;
  168. if (!len)
  169. return Err();
  170. ++ptr;
  171. if (ptr != s + str.size() && *ptr == '+') {
  172. ++ptr;
  173. if (ptr != s + str.size() && *ptr == '-')
  174. return Err();
  175. }
  176. int exp;
  177. auto [finish, ec] = std::from_chars(ptr, s + str.size(), exp);
  178. if (ec != std::errc() || finish != s + str.size())
  179. return Err();
  180. const int p = precision, s = int(scale) + exp;
  181. const auto r = exp > 0 ?
  182. FromString(str.Head(len), precision, std::min(s, p)):
  183. FromString(str.Head(len), std::min(p - exp, int(MaxPrecision)), std::max(s, 0));
  184. if (IsError(r) || IsNan(r)) {
  185. return Err();
  186. }
  187. if (IsInf(r)) {
  188. auto p = str.data();
  189. if (*p == '+' || *p == '-')
  190. ++p;
  191. if (!std::isdigit(*p))
  192. return Err();
  193. return r;
  194. }
  195. if (const auto e = exp > 0 ? std::max(0, s - p) : std::min(0, s)) {
  196. if (r) {
  197. if (exp > 0)
  198. return Mul(r, GetDivider(+e));
  199. if (exp < 0)
  200. return Div(r, GetDivider(-e));
  201. }
  202. }
  203. return r;
  204. }
  205. }
  206. return FromString(str, precision, scale);
  207. }
  208. bool IsValid(const TStringBuf& str) {
  209. auto s = str.data();
  210. auto l = str.size();
  211. if (!s || !l)
  212. return false;
  213. if ('-' == *s || '+' == *s) {
  214. ++s;
  215. --l;
  216. }
  217. if (3U == l && (IsInf(s) || IsNan(s))) {
  218. return true;
  219. }
  220. for (bool dot = false; l--;) {
  221. const char c = *s++;
  222. if (c == '.') {
  223. if (dot)
  224. return false;
  225. dot = true;
  226. continue;
  227. }
  228. if (!std::isdigit(c))
  229. return false;
  230. }
  231. return true;
  232. }
  233. TInt128 Mod(TInt128 a, TInt128 b) {
  234. if (!b || !(IsNormal(a) && IsNormal(b)))
  235. return Nan();
  236. return a % b;
  237. }
  238. TInt128 Div(TInt128 a, TInt128 b) {
  239. if (IsNan(a) || IsNan(b))
  240. return Nan();
  241. if (!b) {
  242. if (a > 0)
  243. return Inf();
  244. else if (a < 0)
  245. return -Inf();
  246. else
  247. return Nan();
  248. } else if (IsInf(b)) {
  249. return IsInf(a) ? Nan() : TInt128(0);
  250. } else if (IsInf(a)) {
  251. return b > 0 ? a : -a;
  252. }
  253. if (b & 1)
  254. a = TUint128(a) << 1U;
  255. else
  256. b >>= 1;
  257. auto d = a / b;
  258. if (d & 1) {
  259. if (const auto m = a % b) {
  260. if (m > 0) ++d;
  261. // else --d;
  262. } else {
  263. if (d & 2) ++d;
  264. }
  265. }
  266. return d >>= 1;
  267. }
  268. namespace {
  269. using TInt256 = TWide<TInt128, TInt128, TUint128>;
  270. TInt128 Normalize(const TInt256& v) {
  271. static const TInt256 PInf256(+Inf()), NInf256(-Inf());
  272. if (v > PInf256)
  273. return +Inf();
  274. if (v < NInf256)
  275. return -Inf();
  276. return *reinterpret_cast<const TInt128*>(&v);
  277. }
  278. constexpr auto HalfBitSize = sizeof(TUint128) << 2U;
  279. TUint128 GetUpperHalf(const TUint128& v) {
  280. return v >> HalfBitSize;
  281. }
  282. TUint128 GetLowerHalf(const TUint128& v) {
  283. return v & TUint128(0xFFFFFFFFFFFFFFFFULL);
  284. }
  285. TInt256 WidenMul(const TInt128& lhs, const TInt128& rhs) {
  286. const bool nl = lhs < 0;
  287. const bool nr = rhs < 0;
  288. const TUint128 l = nl ? -lhs : +lhs;
  289. const TUint128 r = nr ? -rhs : +rhs;
  290. const TUint128 lh[] = {GetLowerHalf(l), GetUpperHalf(l)};
  291. const TUint128 rh[] = {GetLowerHalf(r), GetUpperHalf(r)};
  292. const TUint128 prods[] = {lh[0] * rh[0], lh[0] * rh[1], lh[1] * rh[0], lh[1] * rh[1]};
  293. const TUint128 fourthQ = GetLowerHalf(prods[0]);
  294. const TUint128 thirdQ = GetUpperHalf(prods[0]) + GetLowerHalf(prods[1]) + GetLowerHalf(prods[2]);
  295. const TUint128 secondQ = GetUpperHalf(thirdQ) + GetUpperHalf(prods[1]) + GetUpperHalf(prods[2]) + GetLowerHalf(prods[3]);
  296. const TUint128 firstQ = GetUpperHalf(secondQ) + GetUpperHalf(prods[3]);
  297. const TInt256 combine((firstQ << HalfBitSize) | GetLowerHalf(secondQ), (thirdQ << HalfBitSize) | fourthQ);
  298. return nl == nr ? +combine : -combine;
  299. }
  300. template<bool MayOddDivider>
  301. TInt256 Div(TInt256&& a, TInt256&& b) {
  302. if (MayOddDivider && b & 1)
  303. a <<= 1;
  304. else
  305. b >>= 1;
  306. auto d = a / b;
  307. if (d & 1) {
  308. if (const auto m = a % b) {
  309. if (m > 0) ++d;
  310. // else --d;
  311. } else {
  312. if (d & 2) ++d;
  313. }
  314. }
  315. return d >>= 1;
  316. }
  317. }
  318. TInt128 Mul(TInt128 a, TInt128 b) {
  319. if (IsNan(a) || IsNan(b))
  320. return Nan();
  321. if (IsInf(a))
  322. return !b ? Nan() : (b > 0 ? a : -a);
  323. if (IsInf(b))
  324. return !a ? Nan() : (a > 0 ? b : -b);
  325. return Normalize(WidenMul(a, b));
  326. }
  327. TInt128 MulAndDivNormalMultiplier(TInt128 a, TInt128 b, TInt128 c) {
  328. if (IsNan(a) || IsNan(c))
  329. return Nan();
  330. if (!c) {
  331. if (a > 0)
  332. return Inf();
  333. else if (a < 0)
  334. return -Inf();
  335. else
  336. return Nan();
  337. } else if (IsInf(c)) {
  338. return IsInf(a) ? Nan() : TInt128(0);
  339. } else if (IsInf(a)) {
  340. return c > 0 ? a : -a;
  341. }
  342. return Normalize(Div<true>(WidenMul(a, b), TInt256(c)));
  343. }
  344. TInt128 MulAndDivNormalDivider(TInt128 a, TInt128 b, TInt128 c) {
  345. if (IsNan(a) || IsNan(b))
  346. return Nan();
  347. if (IsInf(a))
  348. return !b ? Nan() : (b > 0 ? a : -a);
  349. if (IsInf(b))
  350. return !a ? Nan() : (a > 0 ? b : -b);
  351. return Normalize(Div<false>(WidenMul(a, b), TInt256(c)));
  352. }
  353. }
  354. }