Search code examples
rustfloating-pointethereumfixed-point

How to convert from Q64.96 represented as bytes to BigFloat in Rust


I am trying to convert from a Q64.96 to a BigFloat. The Q64.96 number is initially represented as a U256 type from the ethers-rs crate.

I first convert the value to big endian bytes. Then I am trying to convert to BigFloat from the num-bigfloat crate.

To do this, I am using the BigFloat::from_bytes associated function but I am not sure how to derive the exponent from bytes when the number represented as a Q64.96. Can someone help me figure out how to convert this value to BigFloat?


Solution

  • I don't think from_bytes is what you want here. Given that what's stored in the U256 is essentially a 160-bit unsigned integer, I'd construct the BigFloat directly from the constituent u64s like so:

    fn convert(q64_96: U256) -> BigFloat {
        let least_sig = q64_96.0[0];
        let second_sig = q64_96.0[1];
        let third_sig = q64_96.0[2];
        let most_sig = q64_96.0[3];
    
        let bf2 = BigFloat::from(2);
        let bf64 = BigFloat::from(64);
        let bf128 = BigFloat::from(128);
        let bf192 = BigFloat::from(192);
        let bf96 = BigFloat::from(96);
    
        (
            (BigFloat::from(most_sig) * bf2.pow(&bf192)) +
            (BigFloat::from(third_sig) * bf2.pow(&bf128)) +
            (BigFloat::from(second_sig) * bf2.pow(&bf64)) +
            BigFloat::from(least_sig)
        ) / bf2.pow(&bf96)
    }
    

    However, it may not be perfectly accurate due to how floats work.

    I'm curious what your use case for this conversion is. Fixed-point arithmetic is generally more predictable, and the Q64.96 has a ton of precision.