decimal.odin 4.5 KB

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