Skip to content

Truncate integer division and remainder in Python and JavaScript - #188

Merged
matt-edmondson merged 1 commit into
mainfrom
fix/114-integer-division
Oct 7, 2026
Merged

matt-edmondson merged 1 commit into
mainfrom
fix/114-integer-division

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Fixes #114

Problem

The AST means / and % on two integers the way C, C++, C#, Go and Rust do: the quotient truncates toward zero and the remainder takes the dividend's sign. Python's and JavaScript's / are true division, and Python's % floors. So -7 / 2 came out as -3.5 in both targets (a float from a function annotated -> int), and Python gave 1 for -7 % 2 instead of -1.

Change

  • StandardLanguageGenerator now has a GenerateBinary hook, an IsIntegerArithmetic check built on the existing StaticType inference, and an OpenStaticTypeScope guard.
  • Python and JavaScript now open the static-type scope that Go already uses, in functions and in methods, and register typed locals. That way parameters and locals are known to be integers.
  • Python, when both operands are statically integers:
    • Division is written as int(a / b). It isn't //, which floors.
    • Remainder is written as (a - b * int(a / b)). If an operand is anything other than a literal or a variable, it goes through (lambda a, b: …)(x, y) so it is evaluated only once. math.fmod isn't used because an expression can be generated outside a file that could carry the import.
  • JavaScript: division is written as Math.trunc(a / b). JavaScript's % already truncates, so it is unchanged.
  • If an operand's type is unknown or not an integer, the output is unchanged.

Tests

  • IntegerDivisionTests generates functions that divide integer parameters, integer literals, a typed local, and a call operand. It runs them under real python3 and node with negative and mixed-sign operands, and compares the results with C#'s / and %.
  • The Python test also checks that the result is an int and that the call operand runs exactly once.
  • A third test pins that non-integer division and division with unknown types are left as is.
  • With the generator changes reverted, both runtime tests fail. Full suite: 1092 passed, 0 failed. The build has 0 warnings.

Not covered here: compound assignment (x /= 2 on an int) has the same mismatch in Python. It goes through AssignmentStatement rather than BinaryExpression, and is left for a follow-up.

🤖 Generated with Claude Code

https://claude.ai/code/session_01AXWSrf4nXKEZF5xVNTiVV5


Generated by Claude Code

… [patch]

The AST means / and % on two integers the way the C family does: the
quotient truncates toward zero and the remainder takes the dividend's
sign. Python's and JavaScript's / are true division, and Python's %
floors, so -7 / 2 came out as -3.5 and -7 % 2 as 1.

When both operands are statically integers, Python now writes int(a / b)
and a - b * int(a / b) (bound through a lambda when an operand is not a
literal or a variable, so it is evaluated once), and JavaScript writes
Math.trunc(a / b). Both generators now open the static-type scope Go
already used, so parameters and typed locals are known to be integers.

Fixes #114

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AXWSrf4nXKEZF5xVNTiVV5
@sonarqubecloud

sonarqubecloud Bot commented Oct 7, 2026

Copy link
Copy Markdown

@matt-edmondson
matt-edmondson merged commit 79b0566 into main Oct 7, 2026
14 checks passed
@matt-edmondson
matt-edmondson deleted the fix/114-integer-division branch October 7, 2026 05:36
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.

Integer division and modulo give different results in Python and JavaScript: -7 / 2 returns -3 in C/C++/C#/Go/Rust but -3.5 in Python and JS

2 participants