add trig functions to dec in zig

This commit is contained in:
Brendan Hansknecht 2023-09-16 11:05:44 -07:00
parent ab2ec925a3
commit 67494e00fd
No known key found for this signature in database
GPG key ID: 0EA784685083E75B
2 changed files with 67 additions and 0 deletions

View file

@ -44,6 +44,10 @@ pub const RocDec = extern struct {
return ret;
}
pub fn toF64(dec: RocDec) f64 {
return @intToFloat(f64, dec.num) / comptime @intToFloat(f64, one_point_zero_i128);
}
// TODO: If Str.toDec eventually supports more error types, return errors here.
// For now, just return null which will give the default error.
pub fn fromStr(roc_str: RocStr) ?RocDec {
@ -419,6 +423,67 @@ pub const RocDec = extern struct {
return RocDec{ .num = if (is_answer_negative) -unsigned_answer else unsigned_answer };
}
fn mod2pi(self: RocDec) RocDec {
// This is made to be used before calling trig functions that work on the range 0 to 2*pi.
// It should be reasonable fast (much faster than calling @mod) and much more accurate as well.
// b is 2*pi as a dec. which is 6.2831853071795864769252867665590057684
// as dec is times 10^18 so 6283185307179586476.9252867665590057684
const b0: u64 = 6283185307179586476;
// Fraction that reprensents 64 bits of precision past what dec normally supports.
// 0.9252867665590057684 as binary to 64 places.
const b1: u64 = 0b1110110011011111100101111111000111001010111000100101011111110111;
// This is dec/(b0+1), but as a multiplication.
// So dec * (1/(b0+1)). This is way faster.
const dec = self.num;
const tmp = @intCast(i128, num_.mul_u128(math.absCast(dec), 249757942369376157886101012127821356963).hi >> (190 - 128));
const q0 = if (dec < 0) -tmp else tmp;
const upper = q0 * b0;
var lower: i128 = undefined;
const overflow = @mulWithOverflow(i128, q0, b1, &lower);
// TODO: maybe write this out branchlessly.
// Currently is is probably cmovs, but could be just math?
const q0_sign: i128 =
if (q0 > 0) 1 else -1;
const overflow_val: i128 = if (overflow) q0_sign << 64 else 0;
const full = upper + @intCast(i128, lower >> 64) + overflow_val;
var out = dec - full;
if (out < 0) {
out += b0;
}
return RocDec{ .num = out };
}
// I belive the output of the trig functions is always in range of Dec.
// If not, we probably should just make it saturate the Dec.
// I don't think this should crash or return errors.
pub fn sin(self: RocDec) RocDec {
return fromF64(math.sin(self.mod2pi().toF64())).?;
}
pub fn cos(self: RocDec) RocDec {
return fromF64(math.cos(self.mod2pi().toF64())).?;
}
pub fn tan(self: RocDec) RocDec {
return fromF64(math.tan(self.mod2pi().toF64())).?;
}
pub fn asin(self: RocDec) RocDec {
return fromF64(math.asin(self.toF64())).?;
}
pub fn acos(self: RocDec) RocDec {
return fromF64(math.acos(self.toF64())).?;
}
pub fn atan(self: RocDec) RocDec {
return fromF64(math.atan(self.toF64())).?;
}
};
// A number has `k` trailling zeros if `10^k` divides into it cleanly

View file

@ -22,6 +22,8 @@ comptime {
exportDecFn(dec.toStr, "to_str");
exportDecFn(dec.fromU64C, "from_u64");
exportDecFn(dec.toI128, "to_i128");
exportDecFn(dec.fromF64, "from_f64");
exportDecFn(dec.toF64, "to_f64");
exportDecFn(dec.eqC, "eq");
exportDecFn(dec.neqC, "neq");
exportDecFn(dec.negateC, "negate");