Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to do a 'for' loop with boundary values and step as floating point values?

Tags:

for-loop

rust

I need to implement a for loop that goes from one floating point number to another with the step as another floating point number.

I know how to implement that in C-like languages:

for (float i = -1.0; i < 1.0; i += 0.01) { /* ... */ }

I also know that in Rust I can specify the loop step using step_by, and that gives me what I want if I have the boundary values and step as integers:

#![feature(iterator_step_by)]

fn main() {
    for i in (0..30).step_by(3) {
        println!("Index {}", i);
    }
}

When I do that with floating point numbers, it results in a compilation error:

#![feature(iterator_step_by)]

fn main() {
    for i in (-1.0..1.0).step_by(0.01) {
        println!("Index {}", i);
    }
}

And here is the compilation output:

error[E0599]: no method named `step_by` found for type `std::ops::Range<{float}>` in the current scope
--> src/main.rs:4:26
  |
4 |     for i in (-1.0..1.0).step_by(0.01) {
  |                          ^^^^^^^
  |
  = note: the method `step_by` exists but the following trait bounds were not satisfied:
          `std::ops::Range<{float}> : std::iter::Iterator`
          `&mut std::ops::Range<{float}> : std::iter::Iterator`

How can I implement this loop in Rust?

like image 526
serge1peshcoff Avatar asked Dec 18 '17 11:12

serge1peshcoff


People also ask

Can you use a float in a for loop?

All simple fractions exactly. All decimals precisely, even when the decimals can be represented in a small number of digits. All digits of large values, meaning that incrementing a large floating-point value might not change that value within the available precision.

How do you convert int to float in Rust?

Integer to Float To convert an integer to a float in Rust, use as f64 . This is a useful means to convert from various interchangeable types, you can use inline also.


2 Answers

Another answer using iterators but in a slightly different way playground

extern crate num;
use num::{Float, FromPrimitive};

fn linspace<T>(start: T, stop: T, nstep: u32) -> Vec<T>
where
    T: Float + FromPrimitive,
{
    let delta: T = (stop - start) / T::from_u32(nstep - 1).expect("out of range");
    return (0..(nstep))
        .map(|i| start + T::from_u32(i).expect("out of range") * delta)
        .collect();
}

fn main() {
    for f in linspace(-1f32, 1f32, 3) {
        println!("{}", f);
    }
}

Under nightly you can use the conservative impl trait feature to avoid the Vec allocation playground

#![feature(conservative_impl_trait)]

extern crate num;
use num::{Float, FromPrimitive};

fn linspace<T>(start: T, stop: T, nstep: u32) -> impl Iterator<Item = T>
where
    T: Float + FromPrimitive,
{
    let delta: T = (stop - start) / T::from_u32(nstep - 1).expect("out of range");
    return (0..(nstep))
        .map(move |i| start + T::from_u32(i).expect("out of range") * delta);
}

fn main() {
    for f in linspace(-1f32, 1f32, 3) {
        println!("{}", f);
    }
}
like image 150
user25064 Avatar answered Sep 27 '22 20:09

user25064


If you haven't yet, I invite you to read Goldberg's What Every Computer Scientist Should Know About Floating-Point Arithmetic.

The problem with floating points is that your code may be doing 200 or 201 iterations, depending on whether the last step of the loop ends up being i = 0.99 or i = 0.999999 (which is still < 1 even if really close).

To avoid this footgun, Rust does not allow iterating over a range of f32 or f64. Instead, it forces you to use integral steps:

for i in -100i8..100 {
    let i = f32::from(i) * 0.01;
    // ...
}

See also:

  • How do I convert between numeric types safely and idiomatically?
like image 31
Matthieu M. Avatar answered Sep 27 '22 18:09

Matthieu M.