Skip to content

baitcode/cairo-fixed-point-arithmetic

Repository files navigation

Cairo Fixed Point Arithmetic

Uses 123 bit for integer part and 128 for fractional and packs everything in one felt252



About

This crate was born in an effort to create fixed point datatype for Ekubo Protocol governance. It's specifically taiored to be used in smart-contracts. This library features:

  • Fixed Point datatype that supports operations: multiplication, division, addition and substraction.
  • Data packing and unpacking for smart contract storage.
  • Overflow and underflow checking for all operations.
  • Additional convenince methods for better performance without conversions such as u64 by u128 division method.
  • Conversion from u64, u128 and u256 types.
  • Rounding implementation.

Main type this crate export is:

#[derive(Debug, Drop, Copy, Serde)]
pub struct UFixedPoint123x128 { 
    value: u256
}

Despite the fact this library don't derive starknet::Store it provides fp::UFixedPoint123x128StorePacking implementation for use in contract storage.

Usage

All use-cases are perfectly described by this snippet.

// Basic type import
use fp::{ UFixedPoint123x128 };

// Store packing implementation import
use fp::{ UFixedPoint123x128StorePacking };

// Convenience functions to avoid type conversions
use fp::{
    div_u64_by_u128, 
    div_u64_by_fixed_point, 
    mul_fixed_point_by_u128
}

fn main() {
    // Create a fixed point value 1.0
    let one: UFixedPoint123x128 = 1_u64.into();
    // Create a fixed point value 100.0
    let hundred: UFixedPoint123x128 = 100_u64.into();
    // Calculate a fixed point value 0.01
    let one_over_hundred = one / hundred;
    // OR
    let other_example = div_u64_by_u128(1, 100);

    assert_eq!(one_over_hundred, other_example );
    
    let multiplication_is_supported = one_over_hundred * hundred;
    assert_eq!(multiplication_is_supported, 1_u64.into());
    
    let two: UFixedPoint123x128 = 2_u64.into();
    let three: UFixedPoint123x128 = 3_u64.into();
    let six: UFixedPoint123x128 = 6_u64.into();
    let one_over_three = one / three;
    let one_over_six = one / six;

    // PartialEq is implemented, values are equal if they are close enough.
    // Difference is less than 1 / 2^124
    assert_eq!(one_over_three, one_over_six * two);
}

This crate also provides additional method for UFixedPoint123x128 type through public UFixedPoint123x128Impl which implements UFixedPointTrait.

// Additional methods implementation
use fp::{ UFixedPoint123x128Impl };

UFixedPoint123x128Impl implements UFixedPointTrait 

trait UFixedPointTrait {
    // Returns integer part of the fixed point value
    fn get_integer(self: UFixedPoint123x128) -> u128;

    // Returns fractional part of the fixed point value
    fn get_fractional(self: UFixedPoint123x128) -> u128;
    
    // Rounds fixed point and returns integer part. 0.5 is rounded up.
    fn round(self: UFixedPoint123x128) -> u128;
}

Contributing

[WIP] but feel free to open issue, discuss it with me and submit PR upon assignment.

Special Thanks

Shramee Srivastav
Shramee Srivastav

For submitting bug report on unchecked error during packing due to incorrect MAX_INT value.

PS

Love and peace, dude. Hope it was useful.

Releases

No releases published

Packages

No packages published

Languages