Repository navigation
BUG: Don't produce undefined behavior for a << b if b >= bitsof(a) - #13739
Conversation
|
CI failure is due to this assert: Which can be reprod on godbolt here. An extremely minimal repro is here. @juliantaylor, could you look at reporting this upstream? |
seberg
left a comment
There was a problem hiding this comment.
Looks good to me. I have not worked with those really to be honest, but can't we put a new npy_left_shift to numpy/core/src/npymath/npy_math_internal.h.src or similar to avoid duplication?
|
@seberg: We have no precedent in that file for functions templated over all integer types. The best we have are What names would you propose? |
|
Oh,had not checked, but doesn't sound too bad, the normal type code should suffice, e.g. |
|
Realized that there's precedent for Unfortunately, I strongly suspect this is going to fail CI again (and if it doesn't, then I think we have a performance problem). I think the only fix here is to add a |
|
Looks like this hit a problem on macOS as well. Somehow the FPU flag gets set, but only on computations with |
There was a problem hiding this comment.
I have no idea why this happens. Is our macOS run x86, or something else? Do we have a resident macOS expert?
There was a problem hiding this comment.
I have no idea, was thinking about putting it in godbold, but it is quite a lot of code around it (and I am not sure it is likely to show anything in any case).
@charris, I think I am good with this as is. But you had a look at it before and know npy_math stuff better. The floatstatus thing (and I guess the compile time check) are a bit unfortunate though...
e8fbd2f to
6d4eb24
Compare
|
Coverage stats are just bad because we can't measure coverage for setup.py. ARM CI now passes, because It means 64-bit |
There was a problem hiding this comment.
Do we want to deal with the possibility that b is negative?
There was a problem hiding this comment.
Not that currently the shift value is cast to unsigned and masked, at least on my hardware.
In [1]: a = array([1,2,3,4])
In [2]: a << -1
Out[2]:
array([-9223372036854775808, 0, -9223372036854775808,
0])
There was a problem hiding this comment.
I think there was a comment in the old/new code about that as well. Maybe we can push it off to another PR? That seems the easiest way forward? We can ignore the codecov failures.
|
Fixes #2249 |
|
With the caveat that negative shifts still need to be handled, I think this is good to go as is |
|
I'm OK with this going in. Would be good to have a release note about changed behavior in case it affects somebody downstream. |
|
Waiting on resolution of #13908 to know how to add a release note |
|
@seberg Ping. |
|
@charris thanks. Let me merge the towncrier related PRs (since nobody complained), and then I will add a short release note to this. Other than that I remember this as being good (although that compiler bug is a bit annoying). |
55cf805 to
9f258d8
Compare
|
OK, I rebased this (it was a bit messy) and added a release note (we will have to modify the towncrier template a bit before the end, but it works). Master seems to have added |
|
restarting tests |
This: * Inlines the macros in loops.c.src * Replaces 8 with `CHAR_BIT `. The `NPY_SIZEOF_*` macros are not used here because it's too much work to apply them to the signed types, and they expand to the same thing anyway. * Removes the reduce loop specializations which likely no one cares about * Uses pytest.mark.parametrize to shorten the test
This avoids duplication, as suggested in review. The suffices `l`, `ull` etc are for consistency with `strtoull` and friends. `h` and `uhh` are inspired from the C99 printf specifiers.
…rror Needed to make CI pass
9f258d8 to
6cf6ece
Compare
|
Thanks @eric-wieser |
## Summary `BitShift` currently only accepts unsigned integer types. This adds signed integer types in a new opset-28 schema. Fixes #2406 Will be necessary for #5497 too. ## Motivation `BitShift` is the only bitwise operator still restricted to unsigned types. `BitwiseAnd`, `BitwiseOr`, `BitwiseXor` and `BitwiseNot` already support signed types. a4f44a2 added `BitShift` to support hashing algorithms, which only need unsigned types. But shifting with sign extension may be used in other contexts. See for reference: * Appendix B of https://arxiv.org/abs/1712.05877 * gemmlowp: https://github.com/google/gemmlowp/blob/16e8662c34917be0065110bfcd9cc27d30f52fdf/fixedpoint/fixedpoint.h#L365-L379 * TOSA [ARITHMETIC_RIGHT_SHIFT](https://www.mlplatform.org/tosa/tosa_spec_1_0_0.html#_arithmetic_right_shift) and [RESCALE](https://www.mlplatform.org/tosa/tosa_spec_1_0_0.html#_rescale) definition. * https://arxiv.org/abs/1905.13298 Shifting of signed integer types is available in other frameworks. This reference implementation continues to use NumPy, and that matches PyTorch and JAX too: https://gist.github.com/mcollinswisc/7cc504381d6c30ee50ef72745f47c6e5 (PyTorch bit shift supports the signed dtypes and `uint8`, but *not* `uint16`, `uint32` or `uint64`.) MindSpore and Tensorflow are found to produce different results, but only on shift counts that are out of range. Casting to an unsigned type is not a workaround. For a right shift it turns a (sign-extending) arithmetic shift into a logical shift, which gives a different result for negative input. ## Op Semantics Semantics are chosen to match NumPy (which jax and PyTorch match too). Some of the behavior (namely for shifts Y that are out of range: negative or larger than the bit width of X) is *not* fully documented by NumPy, but this new BitShift spec does describe it. The results of NumPy can be explained entirely from their implementation and well-defined C behavior. The NumPy PR numpy/numpy#13739 ensured this: 1. numpy/numpy#13739 added logic that explicitly handles shift amounts Y that are `>=` to the bit width of X 2. While [comments in NumPy](https://github.com/numpy/numpy/blob/5e1d03ffac5f2c0a9c39bfcaa9fc853b2b83151e/numpy/_core/src/umath/loops_autovec.dispatch.c.src#L22-L23) still say handling of negative shifts is TODO, the `size_t` casts at https://github.com/numpy/numpy/blob/5e1d03ffac5f2c0a9c39bfcaa9fc853b2b83151e/numpy/_core/src/npymath/npy_math_internal.h.src#L687 and https://github.com/numpy/numpy/blob/5e1d03ffac5f2c0a9c39bfcaa9fc853b2b83151e/numpy/_core/src/npymath/npy_math_internal.h.src#L697 do deterministically define the behavior: because this cast is defined by [C11 6.3.1.3p2](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf) (and continues to be defined in the same way through C23). Negative Y becomes a very large `size_t` value in the `if` condition & the result will be the saturated 0 or -1. The reference implementation & expected test results continue to simply use NumPy. Results are verified more thoroughly against numpy, torch, and JAX in: https://gist.github.com/mcollinswisc/7cc504381d6c30ee50ef72745f47c6e5 The opset-28 definition is a superset of opset-11, so version upgrade is trivial. --------- Signed-off-by: Maxwell D Collins <[email protected]> Signed-off-by: Justin Chu <[email protected]> Co-authored-by: Claude Opus 5 (1M context) <[email protected]> Co-authored-by: Copilot Autofix powered by AI <[email protected]> Co-authored-by: Andreas Fehlner <[email protected]> Co-authored-by: Justin Chu <[email protected]> Co-authored-by: Copilot App <[email protected]> Copilot-Session: e0da9f12-d855-4907-b12e-43cc8a045ef7
This is a continuation of gh-7473. Fixes gh-2449