如何使用边界值和step作为浮点值进行'for'循环?

ser*_*off 3 for-loop rust

我需要实现一个for从一个浮点数到另一个浮点数的循环,其中步长为另一个浮点数.

我知道如何用类C语言实现它:

for (float i = -1.0; i < 1.0; i += 0.01) { /* ... */ }
Run Code Online (Sandbox Code Playgroud)

我也知道在Rust中我可以使用指定循环步骤step_by,如果我有边界值并且步长为整数,那么它给了我想要的东西:

#![feature(iterator_step_by)]

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

当我使用浮点数时,它会导致编译错误:

#![feature(iterator_step_by)]

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

这是编译输出:

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`
Run Code Online (Sandbox Code Playgroud)

如何在Rust中实现此循环?

d2w*_*ber 9

这基本上与已接受的答案相同,但您可能更喜欢写类似的内容:

for i in (-100..100).map(|x| x as f64 * 0.01) {
    println!("Index {}", i);
}
Run Code Online (Sandbox Code Playgroud)


Mat*_* M. 8

如果你还没有,我邀请你阅读Goldberg的每个计算机科学家应该知道的关于浮点运算的内容.

浮点的问题在于,您的代码可能正在进行200或201次迭代,具体取决于循环的最后一步是否结束i = 0.99i = 0.999999(< 1即使非常接近).

为了避免这种情况footgun,锈不允许迭代一个范围f32f64.相反,它会强制您使用整体步骤:

for i in -100..100 {
    let i = i as f32 * 0.01;
    // ...
}
Run Code Online (Sandbox Code Playgroud)

  • 老实说,能够指定浮点步骤和界限的便利性远远超过了多进行一次或少一次迭代的“问题”。我做了很多几何计算,比如沿着一条线搜索标量函数,沿着参数曲线创建点,沿着域离散化连续函数......在所有这些情况下,我不关心迭代次数,我关心步长和界限,由于浮点不精确而多一个或少一个样本不是问题,但从整数到浮点数的转换很烦人。 (5认同)
  • 我不认为这是一把脚枪。Fp 数学仍然是确定性的,我们可以确定编译时的迭代次数(总是 200 或 201,我不确定的唯一原因是我没有费心去计算)。在大多数使用 fp 的情况下,您不需要确切的迭代次数,您只是试图使其在图形或其他东西上有规则间隔的刻度。我能看到的唯一问题是,如果您假设可以容纳一定数量的元素来调整数组大小,但无论如何您都将需要整数来索引该数组。 (2认同)

Ste*_*fan 5

作为一个真正的迭代器:

操场

/// produces: [ linear_interpol(start, end, i/steps) | i <- 0..steps ]
/// (does NOT include "end")
///
/// linear_interpol(a, b, p) = (1 - p) * a + p * b
pub struct FloatIterator {
    current: u64,
    current_back: u64,
    steps: u64,
    start: f64,
    end: f64,
}

impl FloatIterator {
    pub fn new(start: f64, end: f64, steps: u64) -> Self {
        FloatIterator {
            current: 0,
            current_back: steps,
            steps: steps,
            start: start,
            end: end,
        }
    }

    /// calculates number of steps from (end - start) / step
    pub fn new_with_step(start: f64, end: f64, step: f64) -> Self {
        let steps = ((end - start) / step).abs().round() as u64;
        Self::new(start, end, steps)
    }

    pub fn length(&self) -> u64 {
        self.current_back - self.current
    }

    fn at(&self, pos: u64) -> f64 {
        let f_pos = pos as f64 / self.steps as f64;
        (1. - f_pos) * self.start + f_pos * self.end
    }

    /// panics (in debug) when len doesn't fit in usize
    fn usize_len(&self) -> usize {
        let l = self.length();
        debug_assert!(l <= ::std::usize::MAX as u64);
        l as usize
    }
}

impl Iterator for FloatIterator {
    type Item = f64;

    fn next(&mut self) -> Option<Self::Item> {
        if self.current >= self.current_back {
            return None;
        }
        let result = self.at(self.current);
        self.current += 1;
        Some(result)
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let l = self.usize_len();
        (l, Some(l))
    }

    fn count(self) -> usize {
        self.usize_len()
    }
}

impl DoubleEndedIterator for FloatIterator {
    fn next_back(&mut self) -> Option<Self::Item> {
        if self.current >= self.current_back {
            return None;
        }
        self.current_back -= 1;
        let result = self.at(self.current_back);
        Some(result)
    }
}

impl ExactSizeIterator for FloatIterator {
    fn len(&self) -> usize {
        self.usize_len()
    }

    //fn is_empty(&self) -> bool {
    //    self.length() == 0u64
    //}
}

pub fn main() {
    println!(
        "count: {}",
        FloatIterator::new_with_step(-1.0, 1.0, 0.01).count()
    );
    for f in FloatIterator::new_with_step(-1.0, 1.0, 0.01) {
        println!("{}", f);
    }
}
Run Code Online (Sandbox Code Playgroud)