Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support remaining arithmetic operations #32

Open
danlehmann opened this issue Jul 14, 2023 · 8 comments
Open

Support remaining arithmetic operations #32

danlehmann opened this issue Jul 14, 2023 · 8 comments

Comments

@danlehmann
Copy link
Owner

Basic types like u32 have plenty more operations, like:

  • wrapping_add
  • checked_add
  • overflowing_add
  • saturating_add
  • mul_add
  • saturating_add_signed (wow)
  • is_power_of_two

And plenty more. All of those should be supported.

Some of that is already supported through num_traits, but a) not everyone might want that dependency and b) that is only a subset of the functionality.

@pickx
Copy link
Contributor

pickx commented Jul 25, 2023

as a starting point, here's a list of methods currently implemented for u32 but not UInt:

checked versions of existing operations

  • checked_add
  • checked_div
  • checked_mul
  • checked_shl
  • checked_shr
  • checked_sub

unchecked versions of existing operations

  • unchecked_add
  • unchecked_mul
  • unchecked_shl
  • unchecked_shr
  • unchecked_sub

wrapping versions of existing operations

  • wrapping_add
  • wrapping_div
  • wrapping_mul
  • wrapping_shl
  • wrapping_shr
  • wrapping_sub

overflowing versions of existing operations

  • overflowing_div
  • overflowing_add
  • overflowing_mul
  • overflowing_shl
  • overflowing_shr
  • overflowing_sub

saturating versions of existing operations

  • saturating_add
  • saturating_div
  • saturating_mul
  • saturating_pow
  • saturating_sub

carrying versions of existing operations

  • carrying_add
  • carrying_mul

neg

  • neg
  • checked_neg
  • overflowing_neg
  • wrapping_neg

rem

  • rem
  • checked_rem
  • overflowing_rem
  • wrapping_rem

rem_euclid

  • rem_euclid
  • checked_rem_euclid
  • overflowing_rem_euclid
  • wrapping_rem_euclid

add_signed

  • saturating_add_signed
  • checked_add_signed
  • overflowing_add_signed
  • wrapping_add_signed

div_euclid

  • div_euclid
  • checked_div_euclid
  • overflowing_div_euclid
  • wrapping_div_euclid

ilog

  • ilog
  • ilog10
  • ilog2
  • checked_ilog
  • checked_ilog10
  • checked_ilog2

pow

  • pow
  • overflowing_pow
  • wrapping_pow
  • checked_pow

next_multiple_of

  • next_multiple_of
  • checked_next_multiple_of
  • checked_next_power_of_two

power_of_two

  • is_power_of_two
  • next_power_of_two
  • wrapping_next_power_of_two

other unimplemented operations

  • from_str_radix

  • abs_diff

  • div_ceil
  • div_floor
  • midpoint

  • borrowing_sub
  • widening_mul

@danlehmann
Copy link
Owner Author

Thanks a lot for collecting those!

@pickx
Copy link
Contributor

pickx commented Jul 28, 2023

@danlehmann I can implement some of these if that's okay with you.

on that note, is there a reason why Mul and Div are not implemented?
also, is there a rationale behind the trait bounds on the T for existing core::ops implementations, some of which are unnecessary for the implementation itself? for example, Shl<usize, Output = T> + Shr<usize, Output = T> + From<u8>?

@danlehmann
Copy link
Owner Author

Yes of course, happy to take patches for those!

I have been thinking about those a bit, so I wanted to write down my thoughts.

For many operations we can defer to the underlying type, e.g. wrapping_add should be straight-forward: just add and apply the MASK.

wrapping_xxx likely needs a two-step process though: 1. Do the wrapping_xxx instruction of the underlying type and b) do a try_new and - if error - consider that a wrap as well. But I think for some things we can do better:

  • overflowing_add: Adding two u7 can not overflow on the first step (adding the bytes), so we could (for add) change that to a wrapping_add. The one exception is types like UInt<u8, 8>, UInt<u32, 32> etc., which are valid. For those it's the other way round: We just need to defer to the underlying type and we're good

@danlehmann
Copy link
Owner Author

on that note, is there a reason why Mul and Div are not implemented?

Just an oversight

also, is there a rationale behind the trait bounds on the T for existing core::ops implementations, some of which are unnecessary for the implementation itself? for example, Shl<usize, Output = T> + Shr<usize, Output = T> + From<u8>?

I put in whatever was needed at the time. At some point the Number trait simplified things somewhat though, so maybe some of those aren't needed any longer. If you find any unnecessary ones, removing should be safe from an api stability viewpoint

danlehmann added a commit that referenced this issue Oct 22, 2023
@danlehmann
Copy link
Owner Author

I implemented a good chunk of them: Mul, MulAssign, Div, DivAssign as well as all wrapping_xxx and saturating_xxx:

#39

For each instruction there are some interesting optimizations: E.g. u4 * u4 can never overflow the underlying u8, where u5*u5 can. I tried to implement those (which are compiler checked) whenever reasonable.

Will do overflowing_xxx and checked_xxx soon as well.

@danlehmann
Copy link
Owner Author

Skipping unchecked_xxx. They are all unstable for now.

In the patch I just referenced I also implemented checked_xxx and overflowing_xxx.

Once that's in, the list of missing operations should be a lot smaller

danlehmann added a commit that referenced this issue Oct 24, 2023
* Implement Mul, MulAssign, Div, DivAssign

Small step towards #32

* Implement wrapping_xxx; fix Shl/Shr semantics in debug builds

- Implement `wrapping_add`, `wrapping_sub`, `wrapping_mul`, `wrapping_div`, `wrapping_shl`, `wrapping_shr`
- In debug builds, `<<` (`Shl`, `ShlAssign`) and `>>` (`Shr`, `ShrAssign`) now bounds-check the shift amount using the same semantics as built-in shifts. For example, shifting a u5 by 5 or more bits will now panic as expected.

* Add saturating_xxx

* Addressed comments

* Fix build in debug mode

* Add checked_xxx and overflowing_xxx

* List all new methods
@danlehmann
Copy link
Owner Author

Landed #39.

This leaves the following:

unchecked versions of existing operations

  • unchecked_add
  • unchecked_mul
  • unchecked_shl
  • unchecked_shr
  • unchecked_sub

carrying versions of existing operations

  • carrying_add
  • carrying_mul

neg

  • neg
  • checked_neg
  • overflowing_neg
  • wrapping_neg

rem

  • rem
  • checked_rem
  • overflowing_rem
  • wrapping_rem

rem_euclid

  • rem_euclid
  • checked_rem_euclid
  • overflowing_rem_euclid
  • wrapping_rem_euclid

add_signed

  • saturating_add_signed
  • checked_add_signed
  • overflowing_add_signed
  • wrapping_add_signed

div_euclid

  • div_euclid
  • checked_div_euclid
  • overflowing_div_euclid
  • wrapping_div_euclid

ilog

  • ilog
  • ilog10
  • ilog2
  • checked_ilog
  • checked_ilog10
  • checked_ilog2

pow

  • pow
  • overflowing_pow
  • wrapping_pow
  • checked_pow

next_multiple_of

  • next_multiple_of
  • checked_next_multiple_of
  • checked_next_power_of_two

power_of_two

  • is_power_of_two
  • next_power_of_two
  • wrapping_next_power_of_two

other unimplemented operations

  • from_str_radix

  • abs_diff

  • div_ceil
  • div_floor
  • midpoint

  • borrowing_sub
  • widening_mul

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants