Как сохранить точность литерала с плавающей точкой в ​​Rust при использовании шаблонов? - PullRequest
0 голосов
/ 09 октября 2019

Какова разумная стратегия для сохранения точности литерала с плавающей точкой в ​​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.

В любом случае, естьсуществует ли разумная стратегия для обеспечения того, чтобы литерал с плавающей запятой конвертировался одинаково в обоих случаях?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...