decimal.ha (4300B)
1 // SPDX-License-Identifier: MPL-2.0 2 // (c) Hare authors <https://harelang.org> 3 // (c) 2010 The Go Authors. All rights reserved. 4 5 def maxshift: u8 = 60; 6 def decimal_point_range: u16 = 2047; 7 8 type decimal = struct { 9 // Numbers 0-9, not ascii, big endian. Length for small numbers is 10 // log10(mantissa * 5^-exp). Subnormal doubles have min exp -1074 and 11 // max mantissa 4e16, giving at most 767 digits. 12 digits: [800]u8, 13 14 // Number of valid digits. May be 0 if the number rounds to 0. 15 nd: size, 16 17 // Decimal point index, may be negative. 18 // -1 means 0.0ddd..., 0 means 0.ddd..., 1 means d.dd..., and so on. 19 dp: i32, 20 21 negative: bool, 22 23 // Were there nonzero digits beyond digits[0..nd]? This affects 24 // rounding. 25 truncated: bool, 26 }; 27 28 // remove trailing zeroes 29 fn trim(d: *decimal) void = { 30 for (d.nd > 0 && d.digits[d.nd - 1] == 0) { 31 d.nd -= 1; 32 }; 33 }; 34 35 fn leftshift_newdigits(d: *decimal, shift: u32) u32 = { 36 shift &= 63; 37 let x_a = left_shift_table[shift]: u32; 38 let x_b = left_shift_table[shift + 1]: u32; 39 let nn = x_a >> 11; 40 let pow5_a = 0x7FF & x_a, pow5_b = 0x7FF & x_b; 41 const p5 = pow5_table[pow5_a..]; 42 let i = 0u32, n = pow5_b - pow5_a; 43 for (i < n; i += 1) { 44 if (i >= d.nd) { 45 return nn - 1; 46 } else if (d.digits[i] == p5[i]) { 47 continue; 48 } else if (d.digits[i] < p5[i]) { 49 return nn - 1; 50 } else { 51 return nn; 52 }; 53 }; 54 return nn; 55 }; 56 57 fn leftshift(d: *decimal, k: u32) void = { 58 assert(k <= maxshift); 59 if (d.nd == 0) return; 60 let nn = leftshift_newdigits(d, k); 61 let r = d.nd: int - 1, w = r: size + nn; 62 let n = 0u64; 63 for (r >= 0) { 64 n += d.digits[r]: u64 << k; 65 const quo = n / 10, rem = n - 10 * quo; 66 if (w < len(d.digits)) { 67 d.digits[w] = rem: u8; 68 } else if (rem != 0) { 69 d.truncated = true; 70 }; 71 n = quo; 72 r -= 1; 73 w -= 1; 74 }; 75 for (n > 0) { 76 const quo = n / 10, rem = n - 10 * quo; 77 if (w < len(d.digits)) { 78 d.digits[w] = rem: u8; 79 } else if (rem != 0) { 80 d.truncated = true; 81 }; 82 n = quo; 83 w -= 1; 84 }; 85 d.nd += nn; 86 if (d.nd > len(d.digits)) { 87 d.nd = len(d.digits); 88 }; 89 d.dp += nn: i32; 90 trim(d); 91 }; 92 93 fn rightshift(d: *decimal, k: u32) void = { 94 let r = 0z, w = 0z, n = 0u64; 95 for (n >> k == 0; r += 1) { 96 if (r >= d.nd) { 97 if (n == 0) { 98 d.nd = 0; 99 return; 100 }; 101 for (n >> k == 0; r += 1) { 102 n *= 10; 103 }; 104 break; 105 }; 106 n = n * 10 + d.digits[r]; 107 }; 108 d.dp -= r: i32 - 1; 109 if (d.dp < -(decimal_point_range: i32)) { 110 *d = decimal { ... }; 111 return; 112 }; 113 const mask = (1u64 << k) - 1; 114 for (r < d.nd; r += 1) { 115 const dig = n >> k; 116 n &= mask; 117 d.digits[w] = dig: u8; 118 w += 1; 119 n = n * 10 + d.digits[r]; 120 }; 121 for (n > 0) { 122 const dig = n >> k; 123 n &= mask; 124 if (w < len(d.digits)) { 125 d.digits[w] = dig: u8; 126 w += 1; 127 } else if (dig > 0) { 128 d.truncated = true; 129 }; 130 n *= 10; 131 }; 132 d.nd = w; 133 trim(d); 134 }; 135 136 // Shift right (k < 0) or left (k > 0). We can only shift up to 60 at a time 137 // without losing bits, so break up big shifts. 138 fn decimal_shift(d: *decimal, k: int) void = { 139 if (d.nd == 0) return; 140 if (k > 0) { 141 for (k > maxshift: int) { 142 leftshift(d, maxshift); 143 k -= maxshift: i32; 144 }; 145 leftshift(d, k: u32); 146 } else if (k < 0) { 147 for (k < -(maxshift: int)) { 148 rightshift(d, maxshift); 149 k += maxshift: i32; 150 }; 151 rightshift(d, (-k): u32); 152 }; 153 }; 154 155 fn should_round_up(d: *decimal, nd: uint) bool = if (nd < d.nd) { 156 if (d.digits[nd] == 5 && nd + 1 == d.nd) { 157 return d.truncated || 158 (nd > 0 && d.digits[nd - 1] & 1 != 0); 159 } else return d.digits[nd] >= 5; 160 } else false; 161 162 fn round(d: *decimal, nd: uint) void = { 163 if (nd >= d.nd) return; 164 if (should_round_up(d, nd)) roundup(d, nd) 165 else rounddown(d, nd); 166 }; 167 168 fn rounddown(d: *decimal, nd: uint) void = { 169 if (nd >= d.nd) return; 170 d.nd = nd; 171 trim(d); 172 }; 173 174 fn roundup(d: *decimal, nd: uint) void = { 175 if (nd >= d.nd) return; 176 for (let i = nd: int - 1; i >= 0; i -= 1) { 177 if (d.digits[i] < 9) { 178 d.digits[i] += 1; 179 d.nd = i: size + 1; 180 return; 181 }; 182 }; 183 d.digits[0] = 1; 184 d.nd = 1; 185 d.dp += 1; 186 }; 187 188 fn decimal_round(d: *decimal) u64 = { 189 if (d.nd == 0 || d.dp < 0) return 0; 190 if (d.dp > 18) return ~0u64; 191 let i = 0z, n: u64 = 0; 192 for (i < d.dp: uint && i < d.nd; i += 1) { 193 n = n * 10 + d.digits[i]; 194 }; 195 for (i < d.dp: uint; i += 1) { 196 n *= 10; 197 }; 198 if (should_round_up(d, d.dp: uint)) { 199 n += 1; 200 }; 201 return n; 202 };