std/bigfloat.zc
Rama main · réplica de lectura del repositorio original.
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<BigFloat> {
let len = strlen(s);
if (len == 0) {
return Result<BigFloat>::Err("BigFloat::from_string: empty string");
}
if (s[0] == '-') {
return Result<BigFloat>::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<char>(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<char>(int_buf);
if (ri.is_err()) {
return Result<BigFloat>::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<char>(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<char>(frac_buf);
if (rf.is_err()) {
return Result<BigFloat>::Err("BigFloat::from_string: invalid fraction part");
}
let frac = rf.unwrap();
mag._mul_pow10_in_place(scale);
mag.add_in_place(&frac);
}
return Result<BigFloat>::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<char>(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<char>(s_digits);
return buf;
}
}