import "./bigint.zc" import "./string.zc" import "./result.zc" import "./mem.zc" struct BigFloat { magnitude: BigInt; scale: int; } impl BigFloat { fn new() -> Self { return BigFloat { magnitude: BigInt::from_int(0), scale: 0 }; } fn from_int(val: u64) -> Self { return BigFloat { magnitude: BigInt::from_int(val), scale: 0 }; } fn from_bigint(val: BigInt, scale: int) -> Self { return BigFloat { magnitude: val, scale: scale }; } fn clone(self) -> Self { return BigFloat { magnitude: self.magnitude.clone(), scale: self.scale }; } fn free(self) { self.magnitude.free(); } // Deprecated alias; use `free`. fn free_mem(self) { self.free(); } fn align_scale(self, target_scale: int) { while self.scale < target_scale { self.magnitude.digits.insert(0, 0); self.scale = self.scale + 1; } } fn add(self, other: BigFloat) -> BigFloat { let s = self.clone(); let o = other.clone(); if s.scale < o.scale { s.align_scale(o.scale); } else if o.scale < s.scale { o.align_scale(s.scale); } s.magnitude.add_in_place(&o.magnitude); let res = BigFloat::from_bigint(s.magnitude.clone(), s.scale); return res; } // internal: multiply the magnitude by 10^n fn _mul_pow10(self, n: int) { for (let i: int = 0; i < n; i = i + 1) { self.magnitude.digits.insert(0, 0); } } // Returns 0 if equal, -1 if this < other, +1 if this > other. fn compare(self, other: BigFloat*) -> int { let a = self.clone(); let b = other.clone(); if (a.scale < b.scale) { a.align_scale(b.scale); } else if (b.scale < a.scale) { b.align_scale(a.scale); } return a.magnitude.compare(&b.magnitude); } // Subtracts other from this (this must be >= other). fn sub(self, other: BigFloat*) -> BigFloat { let a = self.clone(); let b = other.clone(); if (a.scale < b.scale) { a.align_scale(b.scale); } else if (b.scale < a.scale) { b.align_scale(a.scale); } let m = a.magnitude.sub(&b.magnitude); return BigFloat::from_bigint(m, a.scale); } // Multiplies this by other. fn mul(self, other: BigFloat*) -> BigFloat { let m = self.magnitude.mul(&other.magnitude); return BigFloat::from_bigint(m, self.scale + other.scale); } // Divides this by other, producing `precision` fractional digits. fn div(self, other: BigFloat*, precision: int) -> BigFloat { if (other.magnitude.is_zero()) { panic("BigFloat::div: division by zero"); } let a = self.clone(); let b = other.clone(); let shift = precision + b.scale - a.scale; if (shift > 0) { a._mul_pow10(shift); } else if (shift < 0) { b._mul_pow10(-shift); } let q = a.magnitude.div(&b.magnitude); return BigFloat::from_bigint(q, precision); } // Parses a decimal string such as "12.5" into a BigFloat. fn from_string(s: char*) -> Result { let len = strlen(s); if (len == 0) { return Result::Err("BigFloat::from_string: empty string"); } if (s[0] == '-') { return Result::Err("BigFloat::from_string: negative numbers not supported"); } let dot: usize = len; for (let i: usize = 0; i < len; i = i + 1) { if (s[i] == '.') { dot = i; break; } } let int_buf: char* = alloc_n(dot + 1); for (let i: usize = 0; i < dot; i = i + 1) { int_buf[i] = s[i]; } int_buf[dot] = 0; let ri = BigInt::from_string(int_buf); dealloc(int_buf); if (ri.is_err()) { return Result::Err("BigFloat::from_string: invalid integer part"); } let mag = ri.unwrap(); let scale = (int)(len - dot - 1); if (scale > 0) { let frac_buf: char* = alloc_n(scale + 1); for (let i: usize = 0; i < (usize)scale; i = i + 1) { frac_buf[i] = s[dot + 1 + i]; } frac_buf[scale] = 0; let rf = BigInt::from_string(frac_buf); dealloc(frac_buf); if (rf.is_err()) { return Result::Err("BigFloat::from_string: invalid fraction part"); } let frac = rf.unwrap(); mag._mul_pow10_in_place(scale); mag.add_in_place(&frac); } return Result::Ok(BigFloat::from_bigint(mag, scale)); } fn to_string(self) -> char* { let s_digits = self.magnitude.to_string(); let len = strlen(s_digits); if self.scale <= 0 { return s_digits; } let res_len = len + 2; // +1 for '.', +1 for possible leading '0' if (int)len <= self.scale { res_len += self.scale - (int)len + 1; } let buf: char* = alloc_n(res_len + 1); let b_idx: int = 0; let d_idx: int = 0; if (int)len <= self.scale { buf[b_idx] = '0'; b_idx += 1; buf[b_idx] = '.'; b_idx += 1; let i: int = 0; while i < self.scale - (int)len { buf[b_idx] = '0'; b_idx += 1; i += 1; } while d_idx < (int)len { buf[b_idx] = s_digits[d_idx]; b_idx += 1; d_idx += 1; } } else { let dot_pos = (int)len - self.scale; while d_idx < dot_pos { buf[b_idx] = s_digits[d_idx]; b_idx += 1; d_idx += 1; } buf[b_idx] = '.'; b_idx += 1; while d_idx < (int)len { buf[b_idx] = s_digits[d_idx]; b_idx += 1; d_idx += 1; } } buf[b_idx] = 0; dealloc(s_digits); return buf; } }