Какова разумная стратегия для сохранения точности литерала с плавающей точкой в Rust при использовании обобщенных элементов? В качестве примера рассмотрим следующий код:
// External functions
use std::fmt::LowerExp;
// Some random struct parameterized on a float type
struct Foo<Float> {
x: Float,
y: Float,
}
// Some random, generic function
fn bar<Float>(x: Float)
where
Float: LowerExp + From<f32>,
{
let foo = Foo::<Float> { x, y: 0.45.into() };
println!("x = {:.16e} y = {:.16e}", foo.x, foo.y);
}
fn main() {
bar::<f32>(0.45);
bar::<f64>(0.45);
}
Здесь мы получаем вывод:
x = 4.4999998807907104e-1 y = 4.4999998807907104e-1
x = 4.5000000000000001e-1 y = 4.4999998807907104e-1
Это имеет смысл. Первый 0.45
был проанализирован, зная, были ли мы f32
или f64
. Тем не менее, второй был проанализирован как f32
в обоих случаях из-за признака From<f32>
. Теперь я хотел бы, чтобы выходные данные были
x = 4.4999998807907104e-1 y = 4.4999998807907104e-1
x = 4.5000000000000001e-1 y = 4.5000000000000001e-1
, где y
анализируется и устанавливается с использованием точности общего значения Float
. Одна из попыток сделать это - добавить дополнительную черту From<f64>
. Тем не менее, f32
это не удовлетворяет, поэтому мы получаем ошибку компилятора. Если мы удалим этот вариант использования и запустим код:
// External functions
use std::fmt::LowerExp;
// Some random struct parameterized on a float type
struct Foo<Float> {
x: Float,
y: Float,
}
// Some random, generic function
fn bar<Float>(x: Float)
where
//Float: LowerExp + From<f32>,
Float: LowerExp + From<f32> + From<f64>,
{
let foo = Foo::<Float> { x, y: 0.45.into() };
println!("x = {:.16e} y = {:.16e}", foo.x, foo.y);
}
fn main() {
//bar::<f32> (0.45);
bar::<f64>(0.45);
}
, мы получим то, что нам нужно:
x = 4.5000000000000001e-1 y = 4.5000000000000001e-1
за счет того, что он больше не работает на f32
. В качестве альтернативы было предложено использовать ящик num
. К сожалению, если я что-то упустил, это может привести к проблеме с двойным округлением . Программа:
// External functions
use num::FromPrimitive;
use std::fmt::LowerExp;
// Some random struct parameterized on a float type
struct Foo<Float> {
x: Float,
y: Float,
}
// Some random, generic function
fn bar<Float>(x: Float)
where
Float: LowerExp + FromPrimitive,
{
let foo = Foo::<Float> {
x,
y: <Float as FromPrimitive>::from_f64(
0.5000000894069671353303618843710864894092082977294921875,
)
.unwrap(),
};
println!("x = {:.16e} y = {:.16e}", foo.x, foo.y);
}
fn main() {
bar::<f32>(0.5000000894069671353303618843710864894092082977294921875f32);
bar::<f64>(0.5000000894069671353303618843710864894092082977294921875f64);
}
Производит
x = 5.0000005960464478e-1 y = 5.0000011920928955e-1
x = 5.0000008940696716e-1 y = 5.0000008940696716e-1
Это проблематично, потому что x
и y
отличаются в случае f32
.
В любом случае, естьсуществует ли разумная стратегия для обеспечения того, чтобы литерал с плавающей запятой конвертировался одинаково в обоих случаях?