MEGA65 / mega65-rom-public

MEGA65 ROM public issue reporting
4 stars 0 forks source link

INT() of a negative number truncates toward 0, unlike C65 and C64 #164

Open dansanderson opened 2 months ago

dansanderson commented 2 months ago

As of ROM 920408, INT(-1.5) returns -1, and generally demonstrates truncation toward zero. This is unlike the C65 910828 and C64, which truncate toward negative infinity, e.g. INT(-1.5) returns -2.

BASIC65 should be consistent with the C65 and C64 behaviors. It's likely that this behavior also messes up some bitplane graphics features, though I don't have a specific one to mention. I tried to draw a COS() graph the other day and saw some erroneous cowlicks in the negative areas, but I haven't researched the actual cause.

dansanderson commented 1 month ago

The new INT() behavior started in 920177. Versions between 173 and 183 didn't get individual commits, so the change is somewhere in here: https://github.com/MEGA65/mega65-rom/commit/8b5b60d3b6b5587bdff409861a3315bd674a6b70

The comment in int: literally describes it as "The Greatest Integer Function," which I assume means the "most positive integer" and not "the best int() function." :) https://github.com/MEGA65/mega65-rom/commit/8b5b60d3b6b5587bdff409861a3315bd674a6b70#diff-0962fe91edbf3288ab1d64a46fca05ebd2a083f6d6243f22b652b16757d12cb0R12263

The previous definition is here: https://github.com/MEGA65/mega65-rom/blob/4e15b7dc57d0ae9468786b4a993e8a31a271a7a3/b65.src#L12281

Or more specifically, this qint function that has since been replaced: https://github.com/MEGA65/mega65-rom/blob/4e15b7dc57d0ae9468786b4a993e8a31a271a7a3/b65.src#L12239

I'm actually a little torn now. If this really was intended as "The Greatest Integer Function," then all Commodore BASICs had this wrong, and this was a fix. I'm wary of replacing a piece of Commodore BASIC so fundamental, but maybe we consider keeping it?

dansanderson commented 1 month ago

c65manualupdated.txt says:

INT -- Greatest integer function

                           INT (expression)

This  function returns the greatest integer less than or equal to the
numeric expression.

which is unhelpfully contradictory in the case of negative numbers. :)

dansanderson commented 4 weeks ago

Very useful C64 wiki page: https://www.c64-wiki.com/wiki/Floating_point_arithmetic

int: ends with an integerized floating point value in FAC, which is a term I just made up to describe what appears to be a special case throughout number handling, when the excess-128 exponent value is \$a0. In other words, the exponent is 32, so the 31 bits of mantissa can be treated as an unsigned integer, and the actual sign is the sign bit. In theory, we can go back to the definition of INT() as floor by extending the new routine to test the sign bit and add one to the mantissa if it's negative. This operation would never overflow the mantissa because you can't store a number that's a fraction lower than the lowest possible integer, which is the only case where floor() would produce a number outside the range.

exp, mod, fpwrt, and sin all call int directly, and other math routines might rely on its result indirectly. Because the developer that changed this left no documentation as to why they changed it, it's not clear whether the current implementations of the rest of the math library rely on the new truncation behavior, or are actually broken until we fix it to be floor again. I noticed some odd behavior graphing sin() that may be explained by the change in int() behavior; see the "Graphing Calculator" code sample in Let's Paint.