Unsigned and Signed#271
Conversation
ab25267 to
58d2fbc
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## dev #271 +/- ##
==========================================
+ Coverage 92.23% 92.30% +0.07%
==========================================
Files 24 24
Lines 1339 1339
Branches 331 331
==========================================
+ Hits 1235 1236 +1
Misses 45 45
+ Partials 59 58 -1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
58d2fbc to
58074e5
Compare
4454dc3 to
5529b4b
Compare
3bb9234 to
a5b17ca
Compare
ktbarrett
left a comment
There was a problem hiding this comment.
The shape looks good.
| for (auto const& bit : other) { | ||
| temp_bit = bit ? 1 : 0; | ||
| value_ = (value_ << 1) | temp_bit; | ||
| } |
There was a problem hiding this comment.
Welp, I guess we should move BitArray over to Bits as well now.
There was a problem hiding this comment.
Wrap BitArray around Bits? A packed Bitarray rework was mentioned in the unsigned signed docs. That is a similar thing?
There was a problem hiding this comment.
Yeah it's similar. Right now the implicit upcast is kind of an expensive operation. If everything was just Bits under the hood, we can just copy/move the Bits object around.
eb4e00a to
a8c22fd
Compare
a8c22fd to
d9c4995
Compare
|
Follow ups would be
|
| if (storage_ == other.storage_) { | ||
| return std::strong_ordering::equal; | ||
| } | ||
| if (storage_ < other.storage_) { | ||
| return std::strong_ordering::less; | ||
| } | ||
| return std::strong_ordering::greater; |
There was a problem hiding this comment.
Seems like you could just implement operator<=> for BigInt and leave the else branch as the only implementation.
|
Sorry started to review, but got side-tracked, will have time tomorrow as I'm almost done my work for the week. |
| @@ -0,0 +1,422 @@ | |||
| #include <gtest/gtest.h> | |||
There was a problem hiding this comment.
| #include <gtest/gtest.h> | |
| // LCOV_EXCL_BR_START -- gtest macros generate noisy uncovered branches | |
| #include <gtest/gtest.h> |
| struct UnsignedTag {}; | ||
| struct SignedTag {}; |
There was a problem hiding this comment.
Lets not do this this way. This basically works because of EBO, but if we don't get EBO or the optimizer gets confused we break autovectorization.
We can accomplish the same thing with a template variable specialization:
template <typename T>
inline constexpr bool is_coconext_unsigned_v = false;
template <Range R>
inline constexpr bool is_coconext_unsigned_v<detail::Unsigned<R>> = true;
ktbarrett
left a comment
There was a problem hiding this comment.
The big issue is the order of operations issue in in-place operations. Everything else is kinda nitpicking.
| template <size_t W> | ||
| constexpr Signed(Bits<W> const& val) { | ||
| static_assert(W == R.length(), "Construction from Bits requires identical width"); | ||
| value_ = val; | ||
| } |
There was a problem hiding this comment.
Bits is in the detail namespace. I don't think we should be having public methods get or return Bits.
| for (auto& bit : temp) { | ||
| if constexpr (!Bits<R.length()>::is_not_native_int) { | ||
| bit = to_bit(((value_.srl(bit_idx)) & temp_bit).raw()); | ||
| } else { | ||
| bit = to_bit(((value_.srl(bit_idx)) & temp_bit).raw().get_word(0)); | ||
| } | ||
| if (bit_idx > 0) { | ||
| bit_idx--; | ||
| } | ||
| } |
There was a problem hiding this comment.
This is very inefficient, but that will change once the BitArray specialization is in. I wonder if we should just be doing that change first to prevent churn?
| constexpr Range R_res = detail::int_downto_range(R.length() + 1); | ||
| auto ext = resize<R_res.length()>(*this); | ||
| return Signed<R_res>(detail::Bits<R_res.length()>(0) - ext.value_); |
There was a problem hiding this comment.
Probably more simply stated as return Signed<1>(0) - *this
| template <Range R2> | ||
| constexpr Signed& operator+=(Signed<R2> const& rhs) { | ||
| this->value_ = this->value_ + resize<R.length()>(rhs).value_; | ||
| return *this; | ||
| } | ||
| template <Range R2> | ||
| constexpr Signed& operator-=(Signed<R2> const& rhs) { | ||
| this->value_ = this->value_ - resize<R.length()>(rhs).value_; | ||
| return *this; | ||
| } | ||
| template <Range R2> | ||
| constexpr Signed& operator*=(Signed<R2> const& rhs) { | ||
| this->value_ = this->value_ * resize<R.length()>(rhs).value_; | ||
| return *this; | ||
| } | ||
| template <Range R2> | ||
| constexpr Signed& operator/=(Signed<R2> const& rhs) { | ||
| if (!static_cast<bool>(rhs)) { | ||
| throw std::domain_error("Division by zero"); | ||
| } | ||
| this->value_ = this->value_.sdiv(resize<R.length()>(rhs).value_); | ||
| return *this; | ||
| } | ||
| template <Range R2> | ||
| constexpr Signed& operator%=(Signed<R2> const& rhs) { | ||
| if (!static_cast<bool>(rhs)) { | ||
| throw std::domain_error("Division by zero"); | ||
| } | ||
| this->value_ = this->value_.smod(resize<R.length()>(rhs).value_); | ||
| return *this; | ||
| } |
There was a problem hiding this comment.
The order of operations here is off. We want full-sized operation then resize back down:
*this = resize<R>(*this + rhs);|
|
||
| template <Integer T> | ||
| constexpr Signed& operator+=(T const& rhs) { | ||
| *this += Signed<R>(rhs); |
There was a problem hiding this comment.
Can't assume RHS will fit into the LHS's bounds. This is the same order of operations bug. We want
*this = resize<R>(*this + rhs)| constexpr const_iterator(Signed const* parent, size_t offset) | ||
| : parent_(parent), offset_(offset) {} | ||
|
|
||
| constexpr reference operator*() const { |
There was a problem hiding this comment.
WE are going to need mutable references here at some point, but we can get to that later. WE will need a proper BitProxy class like std::vector uses and that should just go on Bits and we can remove this iterator.
| template <typename Target, typename Source> | ||
| requires detail::is_coconext_signed<Target>::value | ||
| constexpr Target as(Source const& source) noexcept { | ||
| static_assert( | ||
| Target::static_range.length() == Source::static_range.length(), | ||
| "as() requires equal widths." | ||
| ); | ||
| return Target(static_cast<detail::Array<Bit, Source::static_range>>(source)); | ||
| } |
There was a problem hiding this comment.
I think this could be expanded to accept any object with Bits as the implementation type. You could make a detail::uses_Bits trait with Signed, Unsigned, BitArray, and eventually the fixed point and float classes all participating, then you only have one implementation of this function.
Closes #191.
These are useful for implementing a bunch of features currently found in cocotb's
LogicArray(e.g.from_unsigned,to_unsigned) that we for binding coconext'sDynLogicArrayto Python in a way it can sit in place of cocotb's using the patcher.This just uses
uint64_tandint64_t. We will adaptUInt, andSIntfrom #278 once it's done.