Skip to content

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 #114

Description

@matt-edmondson

What's wrong

BinaryOperator.Divide and BinaryOperator.Modulo are spelled / and % for every target: PythonGenerator.GetOperatorSpelling (Coder/Languages/PythonGenerator.cs:803-808) and JavaScriptGenerator.cs:554-558 use them unchanged. The project treats int / int as truncating integer division; FloatingPointLiteralTests exists because 1 / 2 is integer division in C, C++ and Go. But Python's / and JavaScript's / are true division, and Python's % floors. So the same AST computes different values depending on the target language.

Repro

FunctionDeclaration d = new("half") { ReturnType = "int" };
d.Body.Add(new ReturnStatement(new BinaryExpression(
    new LiteralExpression<int>(-7), BinaryOperator.Divide, new LiteralExpression<int>(2))));

Every generator emits return (-7 / 2). Running the emitted code:

Target half() returns
C, C++, C#, Go, Rust (checked with rustc) -3
Python (python3) -3.5, a float from a function annotated -> int
JavaScript (node) -3.5

Modulo also differs: Python evaluates -7 % 2 to 1, where the C family gives -1.

Why it matters

The whole purpose of the tool is that one AST means the same thing in every target. Arithmetic that silently changes value, and in Python's case changes type, is hard to spot in generated code and breaks anything that uses the result, such as indexing or loop bounds.

Suggested fix / acceptance criteria

  • When both operands are statically integers (integer literals and integer-typed variables or parameters, where the type is known), emit truncating division: int(a / b) or math.trunc(a / b) in Python, and Math.trunc(a / b) in JavaScript. Don't use Python's //, because it floors: -7 // 2 == -4.
  • For integer modulo in Python, emit math.fmod(a, b) cast to int, or a - b * int(a / b).
  • If operand types are unknown, document the difference. A generator-level comment would also work.
  • Add tests with negative operands for Divide and Modulo in the Python and JavaScript generators.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions