-
Notifications
You must be signed in to change notification settings - Fork 305
Fix saturating subtraction when the subtrahend is the type minimum #1380
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2419,9 +2419,20 @@ namespace xsimd | |
| template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value>> | ||
| XSIMD_INLINE batch<T, A> ssub(batch<T, A> const& self, batch<T, A> const& other, requires_arch<avx512f>) noexcept | ||
| { | ||
| if (std::is_signed<T>::value) | ||
| // Saturating sub for 8/16-bit integers needs AVX512BW; on AVX512F | ||
| // fall back to the AVX2 implementation on each 256-bit half. | ||
| XSIMD_IF_CONSTEXPR(sizeof(T) == 1 || sizeof(T) == 2) | ||
| { | ||
| return detail::fwd_to_avx([](__m256i s, __m256i o) noexcept | ||
| { return ssub(batch<T, avx2>(s), batch<T, avx2>(o), avx2 {}); }, | ||
| self, other); | ||
| } | ||
| else if (std::is_signed<T>::value) | ||
| { | ||
| return sadd(self, -other); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same here.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it comes with
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. happy to make the 8/16 path fall back to avx instead if you'd prefer |
||
| auto mask = other < 0; | ||
| auto self_overflow_branch = min(std::numeric_limits<T>::max() + other, self); | ||
| auto self_underflow_branch = max(std::numeric_limits<T>::min() + other, self); | ||
| return select(mask, self_overflow_branch, self_underflow_branch) - other; | ||
| } | ||
| else | ||
| { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -720,7 +720,21 @@ namespace xsimd | |
| { | ||
| if (std::numeric_limits<T>::is_signed) | ||
| { | ||
| return sadd(lhs, (T)-rhs); | ||
| // Compute the saturating difference directly. Using sadd(lhs, -rhs) | ||
| // is wrong when rhs == numeric_limits<T>::min(), because -rhs is not | ||
| // representable. | ||
| if ((rhs < 0) && (lhs > std::numeric_limits<T>::max() + rhs)) | ||
| { | ||
| return std::numeric_limits<T>::max(); | ||
| } | ||
| else if ((rhs > 0) && (lhs < std::numeric_limits<T>::lowest() + rhs)) | ||
| { | ||
| return std::numeric_limits<T>::lowest(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why max vs lowest?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. matching the sadd just above it, which uses max() / lowest()
for integers they're identical |
||
| } | ||
| else | ||
| { | ||
| return lhs - rhs; | ||
| } | ||
| } | ||
| else | ||
| { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why directly to commmon here? are there no intrinsics for it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
indeed there's
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right, I've since dropped the common detour and implemented it inline here, mirroring the sadd right above
And yeah, as Serge noted, AVX2 has subs_epi8/epi16, those stay on the native path in
avx2::ssub. This overload only handles the 32/64-bit case, which has no saturating-sub intrinsicThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean you can also emulate saturating using sub + select no? Would that be faster that a scalar loop?