So, I've tried to make a simple math library in Rust. What do you think about it?
const PRECISION: f64=512.; //Balance between speed and precision here.
fn ln(x:f64) -> f64 {
let mut sum=0.;
let epsilon=(x-1.)/(5.*PRECISION);
let mut i=1.;
while (epsilon>0. && i<x) || (epsilon<0. && i>x) {
sum+=epsilon/i;
i+=epsilon;
}
sum
}
fn exp(x:f64) -> f64 {
let mut i=0.;
let mut y=1.;
let epsilon=x/PRECISION;
while (epsilon>0. && i<x) || (epsilon<0. && i>x) {
y+=epsilon*y;
i+=epsilon;
}
y
}
fn arctan(x:f64) -> f64 {
let mut sum=0.;
let epsilon=x/PRECISION;
let mut i=0.;
while i<x {
sum+=epsilon/(1.+i*i);
i+=epsilon;
}
sum*(180./pi())
}
fn tan(degrees:f64) -> f64 {
sin(degrees)/cos(degrees)
}
fn sin(degrees:f64) -> f64 {
if degrees<0. {
return -sin(-degrees);
}
if degrees>90. {
return cos(degrees-90.);
}
let radians=degrees/(180./pi());
let mut tmpsin=0.;
let mut tmpcos=1.;
let epsilon=radians/PRECISION;
let mut i=0.;
while (epsilon>0. && i<radians) || (epsilon<0. && i>radians) {
tmpsin+=epsilon*tmpcos;
tmpcos-=epsilon*tmpsin;
i+=epsilon;
}
tmpsin
}
fn arcsin(x:f64) -> f64 {
arctan(x/sqrt(1.-x*x))
}
fn arccos(x:f64) -> f64 {
90.-arcsin(x)
}
fn cos(degrees:f64) -> f64 {
sin(90.-degrees)
}
fn sqrt(x:f64) -> f64 {
let mut max=1000.;
let mut min=0.;
let mut i=(min+max)/2.;
while (max-min)>1./PRECISION {
if i*i>x {
max=i;
}
else {
min=i;
}
i=(max+min)/2.;
}
i
}
fn pi() -> f64 {
let mut sum=0.;
let mut i=-1.;
let epsilon=1./PRECISION;
while i<1. {
sum+=epsilon/(1.+i*i);
i+=epsilon;
}
2.*sum
}
fn main() {
println!("x\tsin(x)\tcos(x)\ttan(x)\tln(x)\tsqrt(x)\tasin\tacos\tatan");
for i in 0..101 {
print!("{:.4}\t",i);
print!("{:.4}\t",sin(i as f64));
print!("{:.4}\t",cos(i as f64));
if tan(i as f64)<100. && tan(i as f64)> -10. {
print!("{:.4}\t",tan(i as f64));
}
else if tan(i as f64)< -10. {
print!("{:.3}\t",tan(i as f64));
}
else {
print!("inf\t");
}
if ln(i as f64)> -100. {
print!("{:.4}\t",ln(i as f64));
}
else {
print!("-inf\t");
}
print!("{:.4}\t",sqrt(i as f64));
if arcsin((i as f64)/100.)<90. {
print!("{:.4}\t",arcsin((i as f64)/100.));
}
else {
print!("90\t");
}
if arccos((i as f64)/100.)<90. && arccos((i as f64)/100.)>0. {
print!("{:.4}\t",arccos((i as f64)/100.));
}
else if arccos((i as f64)/100.)>0. {
print!("90\t");
}
else {
print!("0\t");
}
print!("{:.4}\n",arctan((i as f64)/100.));
}
println!("pi={:.4}",pi());
println!("rad={:.4}",180./pi());
println!("ln(1/pi)={:.4}",ln(1./pi()));
println!("e={:.4}",exp(1 as f64));
println!("1/e={:.4}",exp(-1 as f64));
}
1 Answer 1
I'm not too familiar with the math side of this, so I'll just focus on the Rust part.
Run rustfmt
Running cargo fmt
will autoformat your code to the Rust best practices. For example, it will convert
fn arctan(x:f64) -> f64 {
let mut sum=0.;
let epsilon=x/PRECISION;
let mut i=0.;
while i<x {
sum+=epsilon/(1.+i*i);
i+=epsilon;
}
sum*(180./pi())
}
to
fn arctan(x: f64) -> f64 {
let mut sum = 0.;
let epsilon = x / PRECISION;
let mut i = 0.;
while i < x {
sum += epsilon / (1. + i * i);
i += epsilon;
}
sum * (180. / pi())
}
Notice the spacing added.
Run clippy
Running cargo clippy
will point out a few common mistakes. Let's take a look at them here.
warning: using `print!()` with a format string that ends in a single newline
--> src/main.rs:136:9
|
136 | print!("{:.4}\n",arctan((i as f64)/100.));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(clippy::print_with_newline)]` on by default
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#print_with_newline
help: use `println!` instead
|
136 | println!("{:.4}",arctan((i as f64)/100.));
| ^^^^^^^
As it suggests, you should replace that line with println!
.
warning: casting integer literal to `f64` is unnecessary
--> src/main.rs:141:28
|
141 | println!("e={:.4}",exp(1 as f64));
| ^^^^^^^^ help: try: `1_f64`
|
= note: `#[warn(clippy::unnecessary_cast)]` on by default
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast
That, and the line that follows it, can be used as a float like you do everywhere else in your code. You can do 1_f64
, 1f64
, 1.0
, or like elsewhere in your code, 1.
.
Extract repeated operations
When you print things out, you repeat the calculation a lot, and it actually appears to have a difference in assembly (although not when #[inline(never)]
is used on ln
?). So instead of
if arccos((i as f64) / 100.) < 90. && arccos((i as f64) / 100.) > 0. {
print!("{:.4}\t", arccos((i as f64) / 100.));
} else if arccos((i as f64) / 100.) > 0. {
print!("90\t");
} else {
print!("0\t");
}
Prefer
let inv_cos = arccos((i as f64) / 100.);
if inv_cos < 90. && inv_cos > 0. {
print!("{:.4}\t", inv_cos);
} else if inv_cos > 0. {
print!("90\t");
} else {
print!("0\t");
}