decimal.odin 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. // Multiple precision decimal numbers
  2. // NOTE: This is only for floating point printing and nothing else
  3. package 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. buf = buf[0:n];
  19. if a.count == 0 {
  20. buf[0] = '0';
  21. return string(buf[0:1]);
  22. }
  23. w := 0;
  24. if a.decimal_point <= 0 {
  25. buf[w] = '0'; w += 1;
  26. buf[w] = '.'; w += 1;
  27. w += digit_zero(buf[w : w-a.decimal_point]);
  28. w += copy(buf[w:], a.digits[0:a.count]);
  29. } else if a.decimal_point < a.count {
  30. w += copy(buf[w:], a.digits[0:a.decimal_point]);
  31. buf[w] = '.'; w += 1;
  32. w += copy(buf[w:], a.digits[a.decimal_point : a.count]);
  33. } else {
  34. w += copy(buf[w:], a.digits[0:a.count]);
  35. w += digit_zero(buf[w : w+a.decimal_point-a.count]);
  36. }
  37. return string(buf[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, i: u64) {
  49. buf: [64]byte;
  50. n := 0;
  51. for 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. delta := int(k/4);
  112. r := a.count; // read index
  113. w := a.count+delta; // write index
  114. n: uint;
  115. for r -= 1; r >= 0; r -= 1 {
  116. n += (uint(a.digits[r]) - '0') << k;
  117. quo := n/10;
  118. rem := n - 10*quo;
  119. w -= 1;
  120. if w < len(a.digits) {
  121. a.digits[w] = byte('0' + rem);
  122. } else if rem != 0 {
  123. a.trunc = true;
  124. }
  125. n = quo;
  126. }
  127. for n > 0 {
  128. quo := n/10;
  129. rem := n - 10*quo;
  130. w -= 1;
  131. if 0 <= w && w < len(a.digits) {
  132. a.digits[w] = byte('0' + rem);
  133. } else if rem != 0 {
  134. a.trunc = true;
  135. }
  136. n = quo;
  137. }
  138. a.count += delta;
  139. a.count = min(a.count, len(a.digits));
  140. a.decimal_point += delta;
  141. trim(a);
  142. }
  143. shift :: proc(a: ^Decimal, k: int) {
  144. uint_size :: 8*size_of(uint);
  145. max_shift :: uint_size-4;
  146. switch {
  147. case a.count == 0:
  148. // no need to update
  149. case k > 0:
  150. for k > max_shift {
  151. shift_left(a, max_shift);
  152. k -= max_shift;
  153. }
  154. shift_left(a, uint(k));
  155. case k < 0:
  156. for k < -max_shift {
  157. shift_right(a, max_shift);
  158. k += max_shift;
  159. }
  160. shift_right(a, uint(-k));
  161. }
  162. }
  163. can_round_up :: proc(a: ^Decimal, nd: int) -> bool {
  164. if nd < 0 || nd >= a.count { return false ; }
  165. if a.digits[nd] == '5' && nd+1 == a.count {
  166. if a.trunc do return true;
  167. return nd > 0 && (a.digits[nd-1]-'0')%2 != 0;
  168. }
  169. return a.digits[nd] >= '5';
  170. }
  171. round :: proc(a: ^Decimal, nd: int) {
  172. if nd < 0 || nd >= a.count { return; }
  173. if can_round_up(a, nd) {
  174. round_up(a, nd);
  175. } else {
  176. round_down(a, nd);
  177. }
  178. }
  179. round_up :: proc(a: ^Decimal, nd: int) {
  180. if nd < 0 || nd >= a.count { return; }
  181. for i := nd-1; i >= 0; i -= 1 {
  182. if c := a.digits[i]; c < '9' {
  183. a.digits[i] += 1;
  184. a.count = i+1;
  185. return;
  186. }
  187. }
  188. // Number is just 9s
  189. a.digits[0] = '1';
  190. a.count = 1;
  191. a.decimal_point += 1;
  192. }
  193. round_down :: proc(a: ^Decimal, nd: int) {
  194. if nd < 0 || nd >= a.count { return; }
  195. a.count = nd;
  196. trim(a);
  197. }
  198. // Extract integer part, rounded appropriately. There are no guarantees about overflow.
  199. rounded_integer :: proc(a: ^Decimal) -> u64 {
  200. if a.decimal_point > 20 {
  201. return 0xffff_ffff_ffff_ffff;
  202. }
  203. i: int = 0;
  204. n: u64 = 0;
  205. m := min(a.decimal_point, a.count);
  206. for ; i < m; i += 1 {
  207. n = n*10 + u64(a.digits[i]-'0');
  208. }
  209. for ; i < a.decimal_point; i += 1 {
  210. n *= 10;
  211. }
  212. if can_round_up(a, a.decimal_point) {
  213. n += 1;
  214. }
  215. return n;
  216. }