Mathematical functions which involve integers.


§1. 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;
];

§2. 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;
];

§3. Absolute value. Of an integer:

[ NUMBER_TY_Abs x; if (x<0) return -x; return x; ];

§4. 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;
];

§5. IntegerRemainder. Similarly.

[ IntegerRemainder A B;
    if (B == 0) { IssueRTP("DividedByZero", "You can't divide by zero.", BasicInformKitRTPs); rfalse; }
    return A%B;
];

§6. 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;
];