Mathematical functions which involve integers.
- §1. Modular arithmetic
- §2. Square Root
- §3. Cube Root
- §4. Absolute value
- §5. IntegerDivide
- §6. IntegerRemainder
- §7. SignedCompare
§1. Modular arithmetic. For every integer n there is a unique congruence class 0, 1, ..., m-1 modulo m to which that integer belongs, and this function returns it. Note that the normal I6 operation % is signed, which is why we need this function.
[ CongruenceClass n modulus; if (modulus > 0) { n = n%modulus; if (n < 0) return n + modulus; } return n; ];
§2. Square Root. We only calculate by hand if we have to.
The slower integer method is an old algorithm for extracting binary square roots, taking 2 bits at a time. We start out with one at the highest bit which isn't the sign bit; that used to be worked out as WORD_HIGHBIT/2, but this caused unexpected portability problems (exposing a minor bug in Inform and also glulxe) because of differences in how C compilers handle signed division of constants in the case where the dividend is \(-2^{31}\), the unique number which cannot be negated in 32-bit twos complement arithmetic.
[ SquareRoot num op res one n x; if (num < 0) { IssueRTP("TookSquareRootOfNegativeNumber", "You can't take the square root of a negative number.", BasicInformKitRTPs); return 1; } n = VM_SquareRoot(num); if (n) return n; op = num; "one" starts at the highest power of four <= the argument. for (one = WORD_NEXTTOHIGHBIT: one > op: one = one/4) ; while (one ~= 0) { if (op >= res + one) { op = op - res - one; res = res/2 + one; } else { res = res/2; } one = one/4; } return res; ];
§3. Cube Root. Again, the technique here is a fallback measure. It's an iterative scheme for finding cube roots by Newton-Raphson approximation, not a great method but which, on the narrow ranges of integers we deal with, just about good enough. The square root is used only as a sighting shot.
[ CubeRoot num x y n; n = VM_CubeRoot(num); if (n) return n; if (num < 0) x = -SquareRoot(-num); else x = SquareRoot(num); for (n=0: (y ~= x) && (n++ < 100): y = x, x = (2*x + num/x/x)/3) ; return x; ];
§4. Absolute value. Of an integer:
[ NUMBER_TY_Abs x; if (x<0) return -x; return x; ];
§5. IntegerDivide. We can't simply use I6's / operator, as that translates directly into a virtual machine opcode which crashes on divide by zero.
[ IntegerDivide A B; if (B == 0) { IssueRTP("DividedByZero", "You can't divide by zero.", BasicInformKitRTPs); rfalse; } return A/B; ];
§6. IntegerRemainder. Similarly.
[ IntegerRemainder A B; if (B == 0) { IssueRTP("DividedByZero", "You can't divide by zero.", BasicInformKitRTPs); rfalse; } return A%B; ];
§7. SignedCompare. This routine is hardly ever needed; it wraps up ordinary comparisons.
[ SignedCompare x y; if (x > y) return 1; if (x == y) return 0; return -1; ];