DAY 020: MOD
#1
(As you guys might have noticed, Keyword of the Day has slowed down and hasn't been being updated on a daily basis for the last few days.  The reason for this is rather simple -- If you check the QB64-PE repo, you'll see that we've been pushing all sorts of different little batches of work into it -- and if you follow our chat on Discord, you'll see that we BROKE QB64-PE.  Matt broke his IDE with some changes...  I broke my IDE with some different changes... and sorting out what went wrong where, has been rather <SIGH> to deal with and sort out all the mess.  There just hasn't been time to sit down and write up a nice Keyword of the Day article, with all the time and effort spent in undoing the glitches that we oh-so-awesomely did.  If anyone else wants to volunteer to do a couple of KotD for us, feel free to speak up and volunteer, and then run with it tomorrow and whenever you get the urge!)

And with that explanation out of the way, let's talk about MOD.

What is it?  It's a very common math function that return the remainder from division.

How does one make use of it?   It's rather simple to implement, just like addition or multiplication.   X = 13 MOD 4..... compare that to....  X = 13 * 4....   Exact same syntax/usage.

So why are we discussing it now?   Because of the topic here: Operator MOD (qb64phoenix.com)


Chris, the original poster of the topic link above, insists that MOD is broken and giving the wrong answer.
  
Quote:There is only one correct result. 

Now, one would think when dealing with math, the above assumption has to be correct.  There can only be one right answer to any mathematical result!   Right?

Then what's the SQR(4)??

QB64 will quickly and happily tell us that the answer is 2!  My math teacher would count that answer as being half wrong, as the answer is BOTH +2 and -2.  (2 * 2 = 4.  -2 * -2 = 4)  Both are valid square roots for SQR(4).   Unfortunately, QB64 only gives us one answer to this function -- the positive value.

By the same token, MOD is one of those operators which can also return different answers.  In fact, various programming languages will each handle the result that it gives you, differently.   13 MOD 4 will *always* be 1, but -7 MOD 5 might be either -2 or 3.

Now, how in the heck does one get those various results??

One language might follow the ruleset (our remainder has to be a value from 0 to our number).  For the language with this ruleset for mod, the answer for -7 MOD 5 would *have to be* 3.  After all, -2 isn't even in the list of possibilities!  It only considers 0 to 4 to be valid remainders for any number divided by 5.  Basically the way they work is:

1) Find the largest multiple of your denominator that is less than the base number, subtract it, and use it to get the remainder.  For 13 MOD 5, it'd find 10 to be the closest multiple of 5 smaller than 13, and then it'd subtract it.   13 - 10 = 3... 3 is the remainder for 13 MOD 5.

Now, in the case of -7 MOD 5, this type of ruleset would choose -10 as the closest multiple of 5, smaller than our number -7, and then it'd subtract it. -7 - -10 = 3.  (negative seven minus negative ten = 3, just in case those signs don't show up readable for anyone.)

That's a perfectly valid interpretation of the answer, and it's not wrong at all.  Unfortunately, it's also not how QB64 (or C, which we translate to by the way) deals with the math, so that's all the explaination I'm going to go into for the other result.  Tongue

2) For QB64 (and for C itself), the rule that is in place for finding the remainder with MOD is basically: Find the closest multiple to your denominator, subtract it, and the result is your remainder.

Now, in the case of 13 MOD 5, the answer is exactly the same.  10 is the closest multiple to 5.  13- 10 = 3.   3 is, of course, the remainder.

But, in the case of -7 MOD 5, we see something different.  -5 is the closest multiple to 5.  -7 - -5 = -2.  -2 is now the answer for us.   <-- This is basically how QB64 and C find their answer.

To help you guys visualize this result, and to showcase that it IS, indeed, a valid answer, let me channel my old math teacher's spirit:

"OK, guys, the first thing you need to realize is that there is no such thing as a negative numbers!"  (I swear, I remember this lecture almost word for word from him, even though I haven't been in his class for over 30 years now.)

"You guys are all broke.  Right?"   (And of course, we'd all nod affirmative.)  "Then let's say I give you guys all $5 each, and you go out and spend it.  How much money do you have after that?"   (He'd give us a moment to think about that, and then continue.)  "You sure as hell don't have NEGATIVE $5 in your pocket.  If you do, pull it out and show it to me!  What you do have, however, is now $5 in debt!   It's a positive number -- just a positive number in a negative direction!!"

"Draw a line from negative 10 to 10 here on the blackboard."

Code: (Select All)
   |.........0.........|
 -10                   10

"Now, count the dots from 5 to -7.  How many of them are there?"

(12, one of us would answer with glee!  Finally a math problem we could know the answer to!)

"And if you make a mark on that graph at every 5 points, how many points are left over between the -5 and the -7?"

(Two!  Two!  Two!  Several of us would now shout the answer to his question.)

"But in what direction is that -7, in relation to your minus 5?"  He'd really make a point to stress this part...

(It's to the left of it!  We'd answer.)

"And left is what, on this line?"  He'd ask, once again giving us a moment to soak in his words.  "It's negative," he'd answer for us.  "That means the answer has to be negative as well -- which makes it negative two. Remember... Negative is just the direction that you're traveling in -- in this case, it's to the left."

-7 MOD 5 = -2.

Which made perfect sense to me, after he explained it in such a simple manner.  The distance between -7 and 5 is 12.  12 MOD 5 is 2...  But it's going in a negative direction, so the answer has to be -2.  

^ And that's basically the logic behind how QB64 and C both come up with their values for MOD.



If one needs positive values as a result from MOD, simply write a small function to get the answer in a format which you can work with:

Code: (Select All)
FUNCTION ModPositive&& (number as _INTEGER64, number2 AS LONG)
    temp&& = number MOD number2 
    IF temp&& < 0 THEN temp&& = temp&& + number
    ModPositive = temp&&
END FUNCTION


All credit for this explaination goes out to the spirit of D.J. Keith -- best math teacher ever!   Any lack of understanding, or failure to pass across his teachings is completely the fault of Pete.  Everyone feel free to blame him.  Wink
Reply
#2
(11-28-2022, 02:00 AM)SMcNeill Wrote: If one needs positive values as a result from MOD, simply write a small function to get the answer in a format which you can work with:

Code: (Select All)
FUNCTION ModPositive&& (number as _INTEGER64, number2 AS LONG)
    temp&& = number MOD number2 
    IF temp&& < 0 THEN temp&& = temp&& + number
    ModPositive = temp&&
END FUNCTION
Disclaimer: in case one wants to blame the forum moderators for "it crashed and I lost my data", there should be a check for "number2" to be zero. Or just let it at the first function line, errrr...
Reply
#3
I find MOD to be an essential tool for game programming. Using MOD I can select individual frames from within a frame counter.

For example, in the Pacman game I'm working on, the game runs at 75 frames per second. I need to know when 25 frames have have passed so I can blink the power pellets on and off. Now I could set up an individual frame counter like so:

PelletFlash = PelletFlash + 1
IF PelletFlash = 25 THEN
    PelletFlash = 0
    ' My code here
END IF

However, when I write games I always created a master frame counter that is keeping track of which frame the game is on. So, I can simply do this:

IF MasterFrame MOD 25 = 0 THEN
    ' My code here
END IF

Same result. much less code.

MOD can be used to skip frames too. Take this for example:

IF MasterFrame MOD 5 THEN
   'My code here
END IF

The above code will skip every number that is evenly divisible by 5 because the IF statement will see the result of 0 (zero) as false.

I've been using MOD with QB64 for years and it works just as it should.
Reply
#4
Yeah so what's -7 mod -5 ? Sorry I think this is funny  Big Grin
b = b + ...
Reply
#5
(11-28-2022, 02:46 AM)bplus Wrote: Yeah so what's -7 mod -5 ? Sorry I think this is funny  Big Grin

I figured the possibility of using negative MOD values into my MODX function. Note, these are not correct MOD values for negative numbers, the function merely preserves the pattern.

Code: (Select All)
FOR i = 5 TO -7 STEP -1
    PRINT i, modx(i, -5)
NEXT

FUNCTION modx (i, j)
    IF SGN(i) < 0 THEN
        IF ABS(i) <> ABS(j) THEN modx = (ABS(j) - ABS(i MOD j)) ELSE modx = 0
    ELSE
        modx = i MOD j
    END IF
END FUNCTION


Pete
Reply
#6
Since I mentioned the use of MOD and files, I might as well toss up an example.

Say we have a file called: "Great-Programs-By-Steve.dat" It's a sequential file made up of the name of the app, the creation date, the file size, and a brief description. so...

Record 1) Program Name
Record 2) Date of Creation
Record 3) File Size
Record 4) Program Description

So let's say all we want is a list of program descriptions.

OPEN "Great-Programs-By-Steve.dat" FOR BINARY AS #1

What? LOF(1) = 0. Well son of a beach!

Okay, Let's change that to "Great-Programs-By-Pete.dat" and make sure we use variable type _INTEGER64 to handle the necessary file size. Big Grin

So...

Code: (Select All)
DIM i AS _INTEGER64
IF _FILEEXISTS("Great-Programs-By-Pete.dat") THEN
    OPEN "Great-Programs-By-Pete.dat" FOR BINARY AS #1
    IF LOF(1) THEN ' Prevents an error if this is an empty file like Steves!
        DO UNTIL EOF(1)
            i = i + 1
            LINE INPUT #1, a$
            IF i MOD 4 = 0 THEN PRINT a$ ' This will print every 4th record to our screen.
        LOOP
    END IF
    CLOSE #1
END IF

Now bugger we have to remember if we want the 4th record of a 4 entry per subject file, you use equals zero instead of equals 4; but hey, that's the way MOD is meant to be used because dammit Jim, it's a math calc, not a tracking cookie.

Of course = 1, = 2, or =3 could be used to find the 1st, 2nd, or 3rd records, in our file, respectively. It's just anytime the it's the last record in the series, in this case 4th of 4, we have to remember to use = 0.

Now get ready for the KEYWORD for tomorrow, POKE; where once again it will be time to POKE fun at Steve! Big Grin

Pete
Reply
#7
wow really nice guide, Steve.  Is all of this building up to the ultimate version of The QB64 Bible?
Reply
#8
MOD is one of my favorite little lesser known tools. I first discovered its utility when trying to do a degree wheel graphic.

Code: (Select All)
SCREEN _NEWIMAGE(640, 640, 32)
WINDOW (-1000, 1000)-(1000, -1000)
CLS
_CLEARCOLOR _RGB32(0, 0, 0)
FOR whl% = 0 TO 359 '                                       iterate through azimuth wheel
    IF whl% MOD 45 = 0 THEN '                               45 degree tick and number
        y = 900
    ELSEIF whl% MOD 10 = 0 THEN '                           10 degree tick
        y = 950
    ELSEIF whl% MOD 5 = 0 THEN '                            5 degree tick
        y = 970
    ELSE '                                                  1 degree tick
        y = 990
    END IF
    'Draw azimuth tick
    LINE (1000 * SIN(_D2R(whl%)), 1000 * COS(_D2R(whl%)))-(y * SIN(_D2R(whl%)), y * COS(_D2R(whl%))), &HAFA800A8
NEXT whl%
DO: LOOP: DO: LOOP
sha_na_na_na_na_na_na_na_na_na:
Reply




Users browsing this thread: 4 Guest(s)