robhagemans / pcbasic

PC-BASIC - A free, cross-platform emulator for the GW-BASIC family of interpreters
http://www.pc-basic.org
Other
393 stars 48 forks source link

MOD operator returns wrong results for negative argument #231

Open tingtron opened 8 months ago

tingtron commented 8 months ago

Bug report

Problem MOD operator returns wrong results with negative argument.

Steps

  1. Run PC-BASIC
  2. Enter the following and press Enter for i=-5 to 3: ? i mod 2; : next
  3. Observed: -1 -2 -1 -2 -1 0 1 0 1
  4. Expected: -1 0 -1 0 -1 0 1 0 1
  5. Compare with GW-BASIC from MS-DOS (in DOSBox)

Note: Python works as expected:

>>> [x % 2 for x in range(-5,3)]
[1, 0, 1, 0, 1, 0, 1, 0]

The problem is in the source pcbasic\basic\values\numbers.py:

    if dividend < 0 or mod < 0:
      mod -= divisor

It should be mod = -mod

Program This affects an existing program, which calculates coordinate offset.

Notes PC-BASIC version: 2.0.7 Operating system version: Windows 11 64-bit

robhagemans commented 8 months ago

I can confirm this is a bug and I can reproduce.

GBR-613 commented 5 months ago

Surprisingly, Python works wrong here:

-17 % 3 1 17 % 3 2 -17 % -3 -2

robhagemans commented 5 months ago

For reference, these are the results for Python and for GW-BASIC:

GW-BASIC:

? 17 mod 3                                                                      
 2                                                                              
Ok                                                                              
? 17 mod -3                                                                     
 2                                                                              
Ok                                                                              
? -17 mod 3                                                                     
-2                                                                              
Ok                                                                              
? -17 mod -3                                                                    
-2                                                                              
Ok     

Python:

>>> 17 % 3
2
>>> 17 % -3
-1
>>> -17 % 3
1
>>> -17 % -3
-2

Some background as to what Python does and why it does that, linked below. TLDR, Python's // rounds toward negative infinity, and % then does what it needs to do to ensure the relation holds that divisor*quotient + remainder = dividend. https://stackoverflow.com/questions/3883004/how-does-the-modulo-operator-work-on-negative-numbers-in-python#3883019 http://python-history.blogspot.com/2010/08/why-pythons-integer-division-floors.html