Skip to content

checked variants of I256 arithmetic functions #1659

@ozgunozerk

Description

@ozgunozerk

What problem does your feature solve?

Right now, we don't have checked_sub, checked_mul, etc. for I256 custom type. This hinders the developers to write additional math libraries that may require the checked versions.

In our case, we are writing a fixed_point_math library, and a WAD equivalent for Soroban. However, especially the error-handling part becomes a bit inconsistent due to lack of checked variants.

Here is a code piece:

pub(crate) fn checked_mul_div_floor(env: &Env, x: &I256, y: &I256, z: &I256) -> Option<I256> {
    let zero = I256::from_i32(env, 0);
    let r = x.mul(y);

    // we handle `DivisionByZero` case, and return `None` as expected by the `checked` variant
    if *z == zero {
        return None;
    }

    if (r < zero && *z > zero) || (r > zero && *z < zero) {
        // ceil is taken by default for a negative result

        // if `rem_euclid` fails, this will panic, but it should have returned `None` instead...
        let remainder = r.rem_euclid(z);
        let one = I256::from_i32(env, 1);

        // similarly, if `div` or `sub` fails, this will panic, but it should have returned `None` instead...
        Some(r.div(z).sub(if remainder > zero { &one } else { &zero }))
    } else {
        // floor is taken by default for a positive or zero result
        Some(r.div(z))
    }
}

What would you like to see?

checked variants of all the arithmetic functions

What alternatives are there?

I saw that, behind the scenes, env.i256_{operation} is used. For example:

    pub fn mul(&self, other: &I256) -> I256 {
        let val = self.env.i256_mul(self.val, other.val).unwrap_infallible();
        I256 {
            env: self.env.clone(),
            val,
        }
    }

However, it is quite unintuitive, and even impossible to use the underlying i256_{operation} functions as alternative.

Impossible, due to:

  • these functions are private, not public

Unintuitive, due to:

  • we have to import i256Val type
  • it is returning Result, and we have to map the Result into Option
  • it is not named as checked, which creates confusion
  • the required argument type I256Val, is reachable only through self.to_val_type(), which adds additional friction

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status

    Backlog (Not Ready)

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions