decimal.odin 4.5 KB

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