hare

[hare] The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

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 };