Floats from bytes in Motoko

I am currently trying to convert a byte array to a Float (16, 32 and 64 bit) without success. I understand the binary structure of a float but not sure how to translate it in motoko.
I don’t see any methods in the Float base module that would help and I’m not even sure where to look. Doesn’t seem like bitshifting out the components will help either but I could be missing something.
Any help would be great

I think I figured it out using bitshifting, needed to approach it from a different angle


    public func decodeFloat(bytes: [Nat8]) : ?Float {
        var bits: Nat64 = Binary.BigEndian.toNat64(bytes);
        let (exponentBitLength: Nat64, mantissaBitLength: Nat64) = switch(bytes.size()) {
            case (2) {
                (5: Nat64, 10: Nat64);
            };
            case (4) {
                (8: Nat64, 23: Nat64);
            };
            case (8) {
                (11: Nat64, 52: Nat64);
            };
            case (a) return null; 
        };
        // Bitshift to get mantissa, exponent and sign bits
        let mantissaBits: Nat64 = bits & (Nat64.pow(2, mantissaBitLength) - 1);
        let exponentBits: Nat64 = (bits >> mantissaBitLength) & (Nat64.pow(2, exponentBitLength) - 1);
        let signBits: Nat64 = (bits >> (mantissaBitLength + exponentBitLength)) & 0x01;

        // Convert bits into numbers
        let e: Int64 = Int64.pow(2, Int64.fromNat64(exponentBits) - ((Int64.fromNat64(Nat64.pow(2, exponentBitLength) / 2)) - 1));
        let maxOffsetInverse: Float = Float.pow(2, Float.fromInt64(Int64.fromNat64(mantissaBitLength)) * -1);
        let m: Float = 1.0 + (Float.fromInt64(Int64.fromNat64(mantissaBits)) * maxOffsetInverse);

        var floatValue: Float = Float.fromInt64(e) * m;

        // Make negative if sign bit is 1
        if (signBits == 1) {
            floatValue := Float.mul(floatValue, -1.0);
        };
        
        ?floatValue;
    }

BUT im still having issues with things like INFINITY and NaN, which dont seem to exist in Motoko.
Anyone have thoughts on those? not sure if it matters

Curious why you cannot pass Float directly as argument?

He’s building CBOR and Candid Interpreters for Motoko so we can use http_outgoing_request from a canister to call services on the IC use t-ECDSA signatures. :grimacing:

1 Like

Okay, then you can use from_candid(blob) : ?Float. Append the blob with prefix DIDL\00\01\72 to indicate this is a float64 value.

2 Likes

Is there a list of those prefixes somewhere?

Yes, it’s in the Candid spec: candid/Candid.md at master · dfinity/candid · GitHub

I wonder how hard it would be to expose that float parsing functionality to Motoko itself

Generally the RTS can do a lot of stuff (it’s written in Rust). But it is a delicate game to only include things that are relevant for the broad community, as bloat will weigh heavy on every installed canister.

Because I was implementing byte encoding libraries, I went ahead and made a library for number encoding/extensions for Nat, Int and Float

2 Likes