decimal.odin 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. // Multiple precision decimal numbers
  2. // NOTE: This is only for floating point printing and nothing else
  3. package strconv_decimal
  4. Decimal :: struct {
  5. digits: [384]byte, // big-endian digits
  6. count: int,
  7. decimal_point: int,
  8. neg, trunc: bool,
  9. }
  10. decimal_to_string :: proc(buf: []byte, a: ^Decimal) -> string {
  11. digit_zero :: proc(buf: []byte) -> int {
  12. for _, i in buf do buf[i] = '0';
  13. return len(buf);
  14. }
  15. n := 10 + a.count + abs(a.decimal_point);
  16. // TODO(bill): make this work with a buffer that's not big enough
  17. assert(len(buf) >= n);
  18. b := buf[0:n];
  19. if a.count == 0 {
  20. b[0] = '0';
  21. return string(b[0:1]);
  22. }
  23. w := 0;
  24. if a.decimal_point <= 0 {
  25. b[w] = '0'; w += 1;
  26. b[w] = '.'; w += 1;
  27. w += digit_zero(b[w : w-a.decimal_point]);
  28. w += copy(b[w:], a.digits[0:a.count]);
  29. } else if a.decimal_point < a.count {
  30. w += copy(b[w:], a.digits[0:a.decimal_point]);
  31. b[w] = '.'; w += 1;
  32. w += copy(b[w:], a.digits[a.decimal_point : a.count]);
  33. } else {
  34. w += copy(b[w:], a.digits[0:a.count]);
  35. w += digit_zero(b[w : w+a.decimal_point-a.count]);
  36. }
  37. return string(b[0:w]);
  38. }
  39. // trim trailing zeros
  40. trim :: proc(a: ^Decimal) {
  41. for a.count > 0 && a.digits[a.count-1] == '0' {
  42. a.count -= 1;
  43. }
  44. if a.count == 0 {
  45. a.decimal_point = 0;
  46. }
  47. }
  48. assign :: proc(a: ^Decimal, idx: u64) {
  49. buf: [64]byte;
  50. n := 0;
  51. for i := idx; i > 0; {
  52. j := i/10;
  53. i -= 10*j;
  54. buf[n] = byte('0'+i);
  55. n += 1;
  56. i = j;
  57. }
  58. a.count = 0;
  59. for n -= 1; n >= 0; n -= 1 {
  60. a.digits[a.count] = buf[n];
  61. a.count += 1;
  62. }
  63. a.decimal_point = a.count;
  64. trim(a);
  65. }
  66. shift_right :: proc(a: ^Decimal, k: uint) {
  67. r := 0; // read index
  68. w := 0; // write index
  69. n: uint;
  70. for ; n>>k == 0; r += 1 {
  71. if r >= a.count {
  72. if n == 0 {
  73. // Just in case
  74. a.count = 0;
  75. return;
  76. }
  77. for n>>k == 0 {
  78. n = n * 10;
  79. r += 1;
  80. }
  81. break;
  82. }
  83. c := uint(a.digits[r]);
  84. n = n*10 + c - '0';
  85. }
  86. a.decimal_point -= r-1;
  87. mask: uint = (1<<k) - 1;
  88. for ; r < a.count; r += 1 {
  89. c := uint(a.digits[r]);
  90. dig := n>>k;
  91. n &= mask;
  92. a.digits[w] = byte('0' + dig);
  93. w += 1;
  94. n = n*10 + c - '0';
  95. }
  96. for n > 0 {
  97. dig := n>>k;
  98. n &= mask;
  99. if w < len(a.digits) {
  100. a.digits[w] = byte('0' + dig);
  101. w += 1;
  102. } else if dig > 0 {
  103. a.trunc = true;
  104. }
  105. n *= 10;
  106. }
  107. a.count = w;
  108. trim(a);
  109. }
  110. shift_left :: proc(a: ^Decimal, k: uint) {
  111. // NOTE(bill): used to determine buffer size required for the decimal from the binary shift
  112. // 'k' means `1<<k` == `2^k` which equates to roundup(k*log10(2)) digits required
  113. log10_2 :: 0.301029995663981195213738894724493026768189881462108541310;
  114. capacity := int(f64(k)*log10_2 + 1);
  115. r := a.count; // read index
  116. w := a.count+capacity; // write index
  117. d := len(a.digits);
  118. n: uint;
  119. for r -= 1; r >= 0; r -= 1 {
  120. n += (uint(a.digits[r]) - '0') << k;
  121. quo := n/10;
  122. rem := n - 10*quo;
  123. w -= 1;
  124. if w < d {
  125. a.digits[w] = byte('0' + rem);
  126. } else if rem != 0 {
  127. a.trunc = true;
  128. }
  129. n = quo;
  130. }
  131. for n > 0 {
  132. quo := n/10;
  133. rem := n - 10*quo;
  134. w -= 1;
  135. if w < d {
  136. a.digits[w] = byte('0' + rem);
  137. } else if rem != 0 {
  138. a.trunc = true;
  139. }
  140. n = quo;
  141. }
  142. // NOTE(bill): Remove unused buffer size
  143. assert(w >= 0);
  144. capacity -= w;
  145. a.count = min(a.count+capacity, d);
  146. a.decimal_point += capacity;
  147. trim(a);
  148. }
  149. shift :: proc(a: ^Decimal, i: int) {
  150. uint_size :: 8*size_of(uint);
  151. max_shift :: uint_size-4;
  152. switch k := i; {
  153. case a.count == 0:
  154. // no need to update
  155. case k > 0:
  156. for k > max_shift {
  157. shift_left(a, max_shift);
  158. k -= max_shift;
  159. }
  160. shift_left(a, uint(k));
  161. case k < 0:
  162. for k < -max_shift {
  163. shift_right(a, max_shift);
  164. k += max_shift;
  165. }
  166. shift_right(a, uint(-k));
  167. }
  168. }
  169. can_round_up :: proc(a: ^Decimal, nd: int) -> bool {
  170. if nd < 0 || nd >= a.count { return false ; }
  171. if a.digits[nd] == '5' && nd+1 == a.count {
  172. if a.trunc do return true;
  173. return nd > 0 && (a.digits[nd-1]-'0')%2 != 0;
  174. }
  175. return a.digits[nd] >= '5';
  176. }
  177. round :: proc(a: ^Decimal, nd: int) {
  178. if nd < 0 || nd >= a.count { return; }
  179. if can_round_up(a, nd) {
  180. round_up(a, nd);
  181. } else {
  182. round_down(a, nd);
  183. }
  184. }
  185. round_up :: proc(a: ^Decimal, nd: int) {
  186. if nd < 0 || nd >= a.count { return; }
  187. for i := nd-1; i >= 0; i -= 1 {
  188. if c := a.digits[i]; c < '9' {
  189. a.digits[i] += 1;
  190. a.count = i+1;
  191. return;
  192. }
  193. }
  194. // Number is just 9s
  195. a.digits[0] = '1';
  196. a.count = 1;
  197. a.decimal_point += 1;
  198. }
  199. round_down :: proc(a: ^Decimal, nd: int) {
  200. if nd < 0 || nd >= a.count { return; }
  201. a.count = nd;
  202. trim(a);
  203. }
  204. // Extract integer part, rounded appropriately. There are no guarantees about overflow.
  205. rounded_integer :: proc(a: ^Decimal) -> u64 {
  206. if a.decimal_point > 20 {
  207. return 0xffff_ffff_ffff_ffff;
  208. }
  209. i: int = 0;
  210. n: u64 = 0;
  211. m := min(a.decimal_point, a.count);
  212. for ; i < m; i += 1 {
  213. n = n*10 + u64(a.digits[i]-'0');
  214. }
  215. for ; i < a.decimal_point; i += 1 {
  216. n *= 10;
  217. }
  218. if can_round_up(a, a.decimal_point) {
  219. n += 1;
  220. }
  221. return n;
  222. }