Wrong use of num::rational?

use bigdecimal::BigDecimal;                                                                      
use num::bigint::BigInt;                                                                         
use num::rational::Ratio;                                                                        
use num::traits::FromPrimitive;                                                                  
use num::traits::ToPrimitive;                                                                    
                                                                                                 
fn main() { 
    {
        // let x: Ratio<BigInt> = Ratio::from_integer(577_u64.into());                           
        // let y: Ratio<BigInt> = Ratio::from_integer(408_u64.into());                           
        let x: Ratio<BigInt> = Ratio::from_float(577.0).unwrap();
        let y: Ratio<BigInt> = Ratio::from_float(408.0).unwrap();
        println!("{:.64}", Ratio::to_f64(&(x / y)).unwrap());
    }

    {   
        let x = BigDecimal::from_f64(577.0).unwrap();
        let y = BigDecimal::from_f64(408.0).unwrap();
        println!("{:.64}", x / y);
    }
}

// rational                                                                                      
// 1.4142156862745098866440685014822520315647125244140625000000000000                            
// decimal                                                                                       
// 1.4142156862745098039215686274509803921568627450980392156862745098                            
// wolfram                                                                                       
// 1.4142156862745098039215686274509803921568627450980392156862745098          

What am I doing wrong?

By calling Ratio::to_f64(), you're rounding 577/408 to the nearest value representable by an f64 before you print it. This can only represent about 16 (decimal) significant digits of a number, but you're printing many more than that.


I'm not too familiar with these libraries, so there's probably a better way to do this, but you can get the correct answer by manually performing the decimal expansion yourself:

        let x: Ratio<BigInt> = Ratio::from_integer(577_i64.into());
        let y: Ratio<BigInt> = Ratio::from_integer(408_i64.into());
        
        let q = x / y;
        println!("{}.{:064}",
                q.trunc(),
                (q.abs().fract() * BigInt::from(10).pow(64)).trunc());
1 Like

That works great, thanks! println! is almost as strange as printf. You can write any number after the comma - the result is always 64 digits.

You’re explicitly asking for that many with {:.64} (or, in my code, {:064}).

1 Like

Yes, that's what I thought too. But e.g. {:032} results in 64 digits and not 32. The same applies to {:032} or whatever. Or to be precise: println!("{}.{:032}".

Ah; I see. In my code, that’s an integer so the field will grow if the number doesn’t fit. For the printed answer to work out correctly, the argument to pow() has to match the field width you give. Otherwise, some zeros immediately after the point might disappear (try 577/408000 for example).

1 Like

Now I see it too: pow(32) . I could have guessed that.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.