Skip to content

fix: handle negative exponents in integer powi - #2043

Merged
laggui merged 3 commits into
tracel-ai:mainfrom
b84330808:powi-int-signed-exponent
Nov 4, 2026
Merged

laggui merged 3 commits into
tracel-ai:mainfrom
b84330808:powi-int-signed-exponent

Conversation

@b84330808

Copy link
Copy Markdown
Contributor

Follow-up to tracel-ai/burn#6355, where @laggui asked to fix signed exponent handling in powi_int and keep the wrapping calculation in a powi_unsigned helper, so Burn can pick the right one per dtype.

Changes

  • powi_int read its i32 exponent as u32, so 3^-1 gave -1431655765 (the inverse of 3 mod 2^32). A negative exponent now gives 1 / base^|exp| truncated toward zero: 1 for a base of 1, 1 or -1 by parity for a base of -1, and 0 otherwise, including a base of 0. That matches Burn's Flex backend and torch.pow on integer tensors. Non-negative exponents are unchanged.
  • For an unsigned base, only 1 stays 1 under a negative exponent. Its maximum value isn't treated as -1.
  • The wrapping calculation is now the public powi_unsigned(base, exp: Vector<u32, N>). Callers with an unsigned exponent keep powers above i32::MAX.

This changes integer powi on every backend that lowers through powi_int (WGSL, SPIR-V, C++, LLVM): a negative exponent used to act like a large unsigned one.

Testing

  • New test_powi_int_negative_exponent fails on main (Values differ: actual=-1431655765, expected=0, index=0) and passes with the fix. test_powi_int now expects truncated results for its negative-exponent cases; on main it fails at (3i64, i32::MIN).
  • New test_powi_unsigned covers exponents above i32::MAX for 8- to 64-bit bases.
  • On Windows with an RTX 5060 Ti, cargo test -p cubecl-wgpu --lib (WGSL, 699 passed) and the same with --features spirv (949 passed) both pass. I couldn't run CUDA, HIP, Metal or the CPU runtime.
  • cargo fmt --check and cargo clippy -p cubecl-core --all-targets --features export_tests -- -D warnings are clean. I didn't run the full cargo xtask validate.
  • With this commit on top of the cubecl rev Burn pins, Burn's int powi tests pass on the Vulkan backend after switching PowiOp to powi_unsigned for unsigned dtypes and powi_int for signed ones. Without that switch, a u32 exponent above i32::MAX gives 0, so the Burn change has to land together with the rev bump.

Validate your PR with burn.

  • Create a new branch or fork of the cubek repo
  • Update the main Cargo.toml with this PR hash.
  • Fix any broken tests or compilation errors in cubek.
  • Submit a PR in cubek with your fixes and link it here.
  • Create a new branch or fork of the burn repo
  • Update the main Cargo.toml with this PR and the cubek PR hash.
  • Fix any broken tests or compilation errors in burn.
  • Submit a PR in burn with your fixes and link it here.

I used Claude Code to write this change.

b84330808 and others added 3 commits November 3, 2026 04:15
`powi_int` read its `i32` exponent as `u32`, so `3^-1` wrapped to
-1431655765 (the inverse of 3 mod 2^32) instead of truncating to 0.

A negative exponent now gives `1 / base^|exp|` truncated toward zero:
1 for a base of 1, 1 or -1 by parity for a base of -1, and 0 otherwise,
including for a base of 0. Non-negative exponents are unchanged.

The wrapping calculation moves to a public `powi_unsigned`, which takes a
`u32` exponent, so callers whose exponent is unsigned (such as Burn's
unsigned integer tensors) keep powers above `i32::MAX`.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

@laggui laggui left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, just consolidated some of the tests

@laggui
laggui merged commit e4c3d3b into tracel-ai:main Nov 4, 2026
7 checks passed
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

Successfully merging this pull request may close these issues.

3 participants