将浮点和从 Python 重写为 Rust
rewriting a floating point sum from Python to Rust
我一直在尝试重写下面的代码以求和浮点数,同时最大限度地减少舍入误差,但我发现在 Rust 中很难做到。任何建议将不胜感激。我附上我的非工作 Rust 尝试
def msum(iterable):
"Full precision summation using multiple floats for intermediate values"
# Rounded x+y stored in hi with the round-off stored in lo. Together
# hi+lo are exactly equal to x+y. The inner loop applies hi/lo summation
# to each partial so that the list of partial sums remains exact.
# Depends on IEEE-754 arithmetic guarantees. See proof of correctness at:
#www-2.cs.cmu.edu/afs/cs/project/quake/public/papers/robust-arithmetic.ps
partials = [] # sorted, non-overlapping partial sums
for x in iterable:
i = 0
for y in partials:
if abs(x) < abs(y):
x, y = y, x
hi = x + y
lo = y - (hi - x)
if lo:
partials[i] = lo
i += 1
x = hi
partials[i:] = [x]
return sum(partials, 0.0)
下面的代码是我目前在 Rust 中的代码,但它还不能工作
fn inexact_sum(v: &Vec<f64>) -> f64 {
let mut partials: Vec<f64> = vec![];
for x in v {
let mut i: usize = 0;
let mut hi: f64;
let mut lo: f64;
for y in partials.clone().iter() {
hi = x + y;
lo = if x.abs() < y.abs() {
y - (hi - x)
} else {
x - (hi - y)
};
if lo != 0.0_f64 {
partials[i] = lo;
i += 1;
}
let x = hi;
println!("x = {}, y = {}", x, y);
}
partials.truncate(i);
partials.push(hi);
}
partials.iter().fold(0.0_f64, |a, b| a + b)
}
编辑:仔细想想,确实,编译器给我的错误error: use of possibly uninitialized variable: `hi`
确实有用。我应该多注意它。关键是第一次循环根本没有执行,所以 hi 没有被初始化。因此,如果我将 partials.push(hi);
更改为 partials.push(*x);
,代码会编译并运行,但不会给出正确的答案。
这是您要找的吗?我认为您并不是要克隆 partials
数组,而是发现您需要这样做才能满足借用检查器的要求;如果您尝试使用代码:
for y in partials.iter() {
...
partials[i] = lo;
借款检查员会抱怨:
<anon>:13:17: 13:25 error: cannot borrow `partials` as mutable because it is also borrowed as immutable [E0502]
我通过在数组中使用索引来解决这个问题:
for j in 0..partials.len() {
let mut y = partials[j];
那么你就不会在外循环中每次都克隆整个部分数组!由于 partials
数组可以在遍历它的同时进行修改,因此首先进行克隆意味着您将得到 y
的旧值而不是新值,如果它已被修改。
use std::mem;
fn msum(v: &[f64]) -> f64 {
let mut partials: Vec<f64> = vec![];
for x in v {
let mut x = *x;
let mut i = 0;
for j in 0..partials.len() {
let mut y = partials[j];
if x.abs() < y.abs() { mem::swap(&mut x, &mut y) }
let hi = x + y;
let lo = y - (hi - x);
if lo != 0.0 {
partials[i] = lo;
i += 1;
}
x = hi;
}
partials.truncate(i);
partials.push(x);
}
partials.iter().fold(0., |a, b| a + b)
}
fn main() {
let v = vec![1.234, 1e16, 1.234, -1e16];
println!("{}",msum(&v));
}
我一直在尝试重写下面的代码以求和浮点数,同时最大限度地减少舍入误差,但我发现在 Rust 中很难做到。任何建议将不胜感激。我附上我的非工作 Rust 尝试
def msum(iterable):
"Full precision summation using multiple floats for intermediate values"
# Rounded x+y stored in hi with the round-off stored in lo. Together
# hi+lo are exactly equal to x+y. The inner loop applies hi/lo summation
# to each partial so that the list of partial sums remains exact.
# Depends on IEEE-754 arithmetic guarantees. See proof of correctness at:
#www-2.cs.cmu.edu/afs/cs/project/quake/public/papers/robust-arithmetic.ps
partials = [] # sorted, non-overlapping partial sums
for x in iterable:
i = 0
for y in partials:
if abs(x) < abs(y):
x, y = y, x
hi = x + y
lo = y - (hi - x)
if lo:
partials[i] = lo
i += 1
x = hi
partials[i:] = [x]
return sum(partials, 0.0)
下面的代码是我目前在 Rust 中的代码,但它还不能工作
fn inexact_sum(v: &Vec<f64>) -> f64 {
let mut partials: Vec<f64> = vec![];
for x in v {
let mut i: usize = 0;
let mut hi: f64;
let mut lo: f64;
for y in partials.clone().iter() {
hi = x + y;
lo = if x.abs() < y.abs() {
y - (hi - x)
} else {
x - (hi - y)
};
if lo != 0.0_f64 {
partials[i] = lo;
i += 1;
}
let x = hi;
println!("x = {}, y = {}", x, y);
}
partials.truncate(i);
partials.push(hi);
}
partials.iter().fold(0.0_f64, |a, b| a + b)
}
编辑:仔细想想,确实,编译器给我的错误error: use of possibly uninitialized variable: `hi`
确实有用。我应该多注意它。关键是第一次循环根本没有执行,所以 hi 没有被初始化。因此,如果我将 partials.push(hi);
更改为 partials.push(*x);
,代码会编译并运行,但不会给出正确的答案。
这是您要找的吗?我认为您并不是要克隆 partials
数组,而是发现您需要这样做才能满足借用检查器的要求;如果您尝试使用代码:
for y in partials.iter() {
...
partials[i] = lo;
借款检查员会抱怨:
<anon>:13:17: 13:25 error: cannot borrow `partials` as mutable because it is also borrowed as immutable [E0502]
我通过在数组中使用索引来解决这个问题:
for j in 0..partials.len() {
let mut y = partials[j];
那么你就不会在外循环中每次都克隆整个部分数组!由于 partials
数组可以在遍历它的同时进行修改,因此首先进行克隆意味着您将得到 y
的旧值而不是新值,如果它已被修改。
use std::mem;
fn msum(v: &[f64]) -> f64 {
let mut partials: Vec<f64> = vec![];
for x in v {
let mut x = *x;
let mut i = 0;
for j in 0..partials.len() {
let mut y = partials[j];
if x.abs() < y.abs() { mem::swap(&mut x, &mut y) }
let hi = x + y;
let lo = y - (hi - x);
if lo != 0.0 {
partials[i] = lo;
i += 1;
}
x = hi;
}
partials.truncate(i);
partials.push(x);
}
partials.iter().fold(0., |a, b| a + b)
}
fn main() {
let v = vec![1.234, 1e16, 1.234, -1e16];
println!("{}",msum(&v));
}