0
\$\begingroup\$

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));
}
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

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");
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.