Skip to content

Commit d8572aa

Browse files
dbrattliclaude
andauthored
[Python] Fix modulo with negative numbers for bigint (#4463)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent ce7eba9 commit d8572aa

4 files changed

Lines changed: 26 additions & 1 deletion

File tree

src/Fable.Cli/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Fixed
1111

12+
* [Python] Fix modulo with negative numbers using Python floored semantics instead of .NET truncated semantics for bigint (fixes #4462) (by @dbrattli)
1213
* [Beam] Fix `System.String.Concat` with 4+ arguments not being supported (by @dbrattli)
1314
* [TS/Python] Fix invalid `this` argument type in structs (#4453) (by @ncave)
1415
* [JS/TS] Fix `N` format specifier (`ToString("N0")`, `String.Format("{0:N0}", ...)`) producing a trailing dot when precision is 0 (fix #2582) (by @MangelMaxime)

src/Fable.Compiler/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Fixed
1111

12+
* [Python] Fix modulo with negative numbers using Python floored semantics instead of .NET truncated semantics for bigint (fixes #4462) (by @dbrattli)
1213
* [Beam] Fix `System.String.Concat` with 4+ arguments not being supported (by @dbrattli)
1314
* [TS/Python] Fix invalid `this` argument type in structs (#4453) (by @ncave)
1415
* [JS/TS] Fix `N` format specifier (`ToString("N0")`, `String.Format("{0:N0}", ...)`) producing a trailing dot when precision is 0 (fix #2582) (by @MangelMaxime)

src/fable-library-py/fable_library/big_int.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,12 @@ def op_division(a: int, b: int) -> int:
8181

8282

8383
def op_modulus(a: int, b: int) -> int:
84-
return a % b
84+
# .NET uses truncated remainder, Python uses floored remainder.
85+
# They differ when the dividend and divisor have different signs.
86+
r = a % b
87+
if r != 0 and ((a < 0) != (b < 0)):
88+
r -= b
89+
return r
8590

8691

8792
def op_right_shift(a: int, num_bits: int) -> int:

tests/Python/TestArithmetic.fs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ let ``test Integer division doesn't produce floats`` () =
6060
let ``test Infix modulo can be generated`` () =
6161
4 % 3 |> equal 1
6262

63+
[<Fact>]
64+
let ``test Infix modulo with negative numbers`` () =
65+
-5 % 3 |> equal -2
66+
5 % -3 |> equal 2
67+
-5 % -3 |> equal -2
68+
6369
// [<Fact>]
6470
// let ``test Math.DivRem works with bytes`` () =
6571
// Math.DivRem(5y, 2y) |> equal struct (2y, 1y)
@@ -384,6 +390,12 @@ let ``test Int64 Integer division doesn't produce floats`` () =
384390
let ``test Int64 Infix modulo can be generated`` () =
385391
4L % 3L |> equal 1L
386392

393+
[<Fact>]
394+
let ``test Int64 Infix modulo with negative numbers`` () =
395+
-5L % 3L |> equal -2L
396+
5L % -3L |> equal 2L
397+
-5L % -3L |> equal -2L
398+
387399
[<Fact>]
388400
let ``test Int64 Evaluation order is preserved by generated code`` () =
389401
(4L - 2L) * 2L + 1L |> equal 5L
@@ -446,6 +458,12 @@ let ``test BigInt Integer division doesn't produce floats`` () =
446458
let ``test BigInt Infix modulo can be generated`` () =
447459
4I % 3I |> equal 1I
448460

461+
[<Fact>]
462+
let ``test BigInt Infix modulo with negative numbers`` () =
463+
-5I % 3I |> equal -2I
464+
5I % -3I |> equal 2I
465+
-5I % -3I |> equal -2I
466+
449467
// [<Fact>]
450468
// let ``test BigInt.DivRem works`` () = // See #1744
451469
// let quotient,remainder = bigint.DivRem(5I,2I)

0 commit comments

Comments
 (0)