Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ New features:

Bugfixes:

- Return zero instead of `NaN` from `Data.Int.rem` when the divisor is zero (#60 by @thomashoneyman).
- Keep the FFI result of `Data.Int.Bits.zshr` within the signed 32-bit `Int` range (#60 by @thomashoneyman). Shifting a negative input by zero or a multiple of 32 now returns that input instead of converting it to an unsigned number.

Other improvements:

## [v6.0.1](https://github.com/purescript/purescript-integers/releases/tag/v6.0.1) - 2026-10-17
Expand Down
2 changes: 1 addition & 1 deletion src/Data/Int.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export const quot = function (x) {

export const rem = function (x) {
return function (y) {
return x % y;
return (x % y) | 0;
};
};

Expand Down
5 changes: 5 additions & 0 deletions src/Data/Int.purs
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,8 @@ fromStringAs = fromStringAsImpl Just Nothing
-- | div 2 (-3) == 0
-- | quot 2 (-3) == 0
-- | ```
-- |
-- | Returns zero when the divisor is zero.
foreign import quot :: Int -> Int -> Int

-- | The `rem` function provides the remainder after _truncating_ integer
Expand All @@ -246,6 +248,9 @@ foreign import quot :: Int -> Int -> Int
-- | mod 2 (-3) == 2
-- | rem 2 (-3) == 2
-- | ```
-- |
-- | Returns zero when the divisor is zero, consistent with `quot` and `mod`.
-- | The quotient/remainder law only applies to nonzero divisors.
foreign import rem :: Int -> Int -> Int

-- | Raise an Int to the power of another Int.
Expand Down
2 changes: 1 addition & 1 deletion src/Data/Int/Bits.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export const shr = function (n1) {

export const zshr = function (n1) {
return function (n2) {
return n1 >>> n2;
return (n1 >>> n2) | 0;
};
};

Expand Down
2 changes: 2 additions & 0 deletions src/Data/Int/Bits.purs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ foreign import shl :: Int -> Int -> Int
foreign import shr :: Int -> Int -> Int

-- | Bitwise zero-fill shift right.
-- | Only the low five bits of the shift count are used. The result is a signed
-- | 32-bit `Int`, so shifting by zero (or a multiple of 32) returns the input.
foreign import zshr :: Int -> Int -> Int

-- | Bitwise NOT.
Expand Down
7 changes: 7 additions & 0 deletions test/Test/Data/Int.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const applyIntBinary = function (f) {
return function (x) {
return function (y) {
return f(x)(y);
};
};
};
42 changes: 42 additions & 0 deletions test/Test/Data/Int.purs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@ module Test.Data.Int (testInt) where
import Prelude

import Data.Int (binary, ceil, even, floor, fromNumber, fromString, fromStringAs, hexadecimal, octal, odd, parity, pow, quot, radix, rem, round, toNumber, toStringAs)
import Data.Int.Bits (zshr)
import Data.Maybe (Maybe(..), fromJust)
import Data.Number (infinity, nan)
import Effect (Effect)
import Effect.Console (log)
import Partial.Unsafe (unsafePartial)
import Test.Assert (assert)

-- Keep calls opaque to the compiler's bitwise-operation inliner so these
-- assertions exercise the library's FFI implementation.
foreign import applyIntBinary :: (Int -> Int -> Int) -> Int -> Int -> Int

testInt :: Effect Unit
testInt = do

Expand Down Expand Up @@ -170,6 +175,43 @@ testInt = do
go 2 (-3)
go (-2) (-3)

log "quot and rem return zero for a zero divisor"
let
testZeroDivisor x = do
assert $ quot x 0 == 0
assert $ rem x 0 == 0
assert $ applyIntBinary rem x 0 == 0
testZeroDivisor 0
testZeroDivisor 5
testZeroDivisor (-5)
testZeroDivisor bottom
testZeroDivisor top

log "rem preserves the dividend's sign"
assert $ applyIntBinary rem (-8) 3 == (-2)
assert $ applyIntBinary rem 8 (-3) == 2
assert $ applyIntBinary rem (-8) (-3) == (-2)

log "zshr FFI returns signed Ints and masks shift counts to five bits"
let
testShiftCount count = do
assert $ applyIntBinary zshr (-1) count == (-1)
assert $ applyIntBinary zshr (-8) count == (-8)
assert $ applyIntBinary zshr bottom count == bottom
assert $ applyIntBinary zshr top count == top
testShiftCount 0
testShiftCount 32
testShiftCount 64
testShiftCount (-32)

log "zshr zero-fills rather than sign-extends negative inputs"
assert $ zshr (-8) 1 == 2147483644
assert $ zshr bottom 31 == 1
assert $ zshr (-8) 33 == 2147483644
assert $ applyIntBinary zshr (-8) 1 == 2147483644
assert $ applyIntBinary zshr bottom 31 == 1
assert $ applyIntBinary zshr (-8) 33 == 2147483644

log "pow"
assert $ pow 2 2 == 4
assert $ pow 5 3 == 125
Expand Down
Loading