Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
361 views
in Technique[技术] by (71.8m points)

Rust: Formatting a Float with a Minimum Number of Decimal Points

I have an f32 value that I'd like to print. Being a float, I can represent integers with this type as well

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;

I'd like to print a value stored as an f32 with a minimum amount of precision, but using as much as necessary to represent the value stored. If my desired minimum precision was one (1), I'd expect the following transformation to be done on the float:

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

I know that I can set a finite number of decimal places using std::fmt's precision, but that doesn't seem to give me what I want. Is there a way to achieve this functionality without bringing in additional formatting crates? Pulling in additional crates isn't out of the realm of possibility, I'm moreso interested in what I'm able to do 'out of the box'

question from:https://stackoverflow.com/questions/65947428/rust-formatting-a-float-with-a-minimum-number-of-decimal-points

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Rust already does this by default. Every float is printed with as many digits as are necessary to denote that particular float uniquely.

Here's a program to demonstrate this. It generates 10000 random floats, converts them to strings, and checks how many digits can be deleted from the fractional part without changing the value.

(Caveat: This does not show that there aren't cases where the number could be represented in fewer digits by rounding it in a different direction, which can happen sometimes if I remember correctly. I'm not a float formatting expert.)

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. Results:

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

The program saw a wide variety of numbers of digits, but never any that could be deleted without changing the answer.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...