Rust:用最少的小数点格式化浮点数

时间:2021-01-29 01:06:01

标签: rust

我有一个要打印的 f32 值。作为浮点数,我也可以用这种类型表示整数

let a_float: f32 = 3.0;
let another_float: f32 = 3.14;
// let's pretend this was user input,
// we didn't know what they'd put enter, so we used f32 to cover our bases
let an_integer: f32 = 3;

我想打印一个以最小精度存储为 f32 的值,但尽可能多地使用来表示存储的值。如果我想要的最小精度是一 (1),我希望在浮点数上完成以下转换:

let a_float: f32 = 3.0;            // print 3.0
let another_float: f32 = 3.14;     // print 3.14
let an_integer: f32 = 3;           // print 3.0

我知道我可以使用 std::fmt's precision 设置有限数量的小数位,但这似乎并没有给我想要的。有没有办法在不引入额外格式化板条箱的情况下实现此功能?拉入额外的板条箱并非不可能,我更感兴趣的是我能够“开箱即用”的东西

1 个答案:

答案 0 :(得分:0)

Rust 默认情况下已经这样做了。每个浮点数都打印了尽可能多的数字,以唯一地表示特定的浮点数。

这是一个演示这一点的程序。它生成 10000 个随机浮点数,将它们转换为字符串,并检查可以在不改变值的情况下从小数部分删除多少位。

(警告:这并不表明没有数字可以通过向不同方向四舍五入以较少位数表示的情况,如果我没记错的话,有时会发生这种情况。我不是浮点格式专家.)

use std::collections::HashMap;
use rand::{Rng, thread_rng};

/// Change this to choose the type analyzed
type Float = f32;

fn main() {
    let mut rng = thread_rng();
    let mut digit_histogram = HashMap::new();
    for _ in 1..10000 {
        let x: Float = rng.gen_range(0.0..10.0);
        let string = x.to_string();
        
        // Break up string representation
        let before_exponent_pos = string.find('e').unwrap_or(string.len());
        let after_decimal_pos = string.find('.')
            .map(|p| p + 1)
            .unwrap_or(before_exponent_pos);
        let prefix = &string[..after_decimal_pos];
        let mut fractional_digits = &string[after_decimal_pos..before_exponent_pos];
        let suffix = &string[before_exponent_pos..];
        
        // What happens if we truncate the digits?
        let initial_digits = fractional_digits.len();
        let mut unnecessary_digits = 0;
        while fractional_digits.len() > 0 {
            fractional_digits = &fractional_digits[..fractional_digits.len() - 1];
            let shortened_string = format!("{}{}{}", 
                prefix,
                fractional_digits,
                suffix,
            );
            let shortened_x = shortened_string.parse::<Float>().unwrap();
            if shortened_x == x {
                unnecessary_digits += 1;
            } else {
                break;
            }
        }

        *(digit_histogram
            .entry((initial_digits, unnecessary_digits))
            .or_insert(0)) += 1;
    }
    
    // Summarize results.
    let mut digit_histogram = digit_histogram.into_iter().collect::<Vec<_>>();
    digit_histogram.sort_by_key(|pair| pair.0);
    for ((initial_digits, unnecessary_digits), occurrences) in digit_histogram {
        println!(
            "{} digits with {} unnecessary × {}",
            initial_digits,
            unnecessary_digits,
            occurrences);
    }
}

Runnable on Rust Playground. 结果:

2 digits with 0 unnecessary × 1
3 digits with 0 unnecessary × 6
4 digits with 0 unnecessary × 25
5 digits with 0 unnecessary × 401
6 digits with 0 unnecessary × 4061
7 digits with 0 unnecessary × 4931
8 digits with 0 unnecessary × 504
9 digits with 0 unnecessary × 62
10 digits with 0 unnecessary × 8

程序看到了各种各样的数字,但从来没有可以在不改变答案的情况下删除的数字。

相关问题