Integer and floating-point calculations translated to C.

§1. Integer arithmetic is handled by the standard operators in C, so this is very easy.

int CArithmetic::compile_primitive(code_generation *gen, inter_ti bip, inter_tree_node *P) {
    text_stream *OUT = CodeGen::current(gen);
    switch (bip) {
        case PLUS_BIP:              WRITE("("); INV_A1; WRITE(" + "); INV_A2; WRITE(")"); break;
        case MINUS_BIP:             WRITE("("); INV_A1; WRITE(" - "); INV_A2; WRITE(")"); break;
        case UNARYMINUS_BIP:        WRITE("(-("); INV_A1; WRITE("))"); break;
        case TIMES_BIP:             WRITE("("); INV_A1; WRITE(" * "); INV_A2; WRITE(")"); break;
        case DIVIDE_BIP:            if (CFunctionModel::inside_function(gen)) {
                                        WRITE("glulx_div_r("); INV_A1; WRITE(", "); INV_A2; WRITE(")");
                                    } else {
                                        WRITE("("); INV_A1; WRITE(" / "); INV_A2; WRITE(")"); break;
                                    }
                                    break;
        case MODULO_BIP:            if (CFunctionModel::inside_function(gen)) {
                                        WRITE("glulx_mod_r("); INV_A1; WRITE(", "); INV_A2; WRITE(")");
                                    } else {
                                        WRITE("("); INV_A1; WRITE(" %% "); INV_A2; WRITE(")");
                                    }
                                    break;
        case BITWISEAND_BIP:        WRITE("(("); INV_A1; WRITE(")&("); INV_A2; WRITE("))"); break;
        case BITWISEOR_BIP:         WRITE("(("); INV_A1; WRITE(")|("); INV_A2; WRITE("))"); break;
        case BITWISENOT_BIP:        WRITE("(~("); INV_A1; WRITE("))"); break;
        case SEQUENTIAL_BIP:        WRITE("("); INV_A1; WRITE(","); INV_A2; WRITE(")"); break;
        case TERNARYSEQUENTIAL_BIP: WRITE("("); INV_A1; WRITE(", "); INV_A2; WRITE(", ");
                                    INV_A3; WRITE(")"); break;
        case RANDOM_BIP:            WRITE("fn_i7_mgl_random(1, "); INV_A1; WRITE(")"); break;
        default:                    return NOT_APPLICABLE;
    }
    return FALSE;
}

§2. Random integers are rather crudely generated for now, in what amounts to a rudimentary form of von Neumann's middle-square algorithm:

int i7_seed = 197;

i7val fn_i7_mgl_random(int n, i7val v) {
    if (i7_seed < 1000) return ((i7val) ((i7_seed++) % n));
    i7_seed = i7_seed*i7_seed;
    return (((i7_seed*i7_seed) & 0xFF00) / 0x100) % n;
}

void glulx_setrandom(i7val x) {
    i7_seed = (int) x;
}

§3. Floating-point calculations are not done by primitives but by the use of Glulx opcodes. (When Inform could only produce code for the Z-machine and Glulx virtual machines, Glulx was the obe of the two which could handle floating-point.)

We emulate these opcodes with a library of functions as follows.

Note that floating-point numbers are stored in i7val values at runtime by storing float (not, alas, double) values as if they were four-byte integers.

void glulx_add(i7val x, i7val y, i7val *z) {
    if (z) *z = x + y;
}

void glulx_sub(i7val x, i7val y, i7val *z) {
    if (z) *z = x - y;
}

void glulx_neg(i7val x, i7val *y) {
    if (y) *y = -x;
}

void glulx_mul(i7val x, i7val y, i7val *z) {
    if (z) *z = x * y;
}

void glulx_div(i7val x, i7val y, i7val *z) {
    if (y == 0) { printf("Division of %d by 0\n", x); if (z) *z = 1; return; }
    int result, ax, ay;
    /* Since C doesn't guarantee the results of division of negative
       numbers, we carefully convert everything to positive values
       first. They have to be unsigned values, too, otherwise the
       0x80000000 case goes wonky. */
    if (x < 0) {
        ax = (-x);
        if (y < 0) {
            ay = (-y);
            result = ax / ay;
        } else {
            ay = y;
            result = -(ax / ay);
        }
    } else {
        ax = x;
        if (y < 0) {
            ay = (-y);
            result = -(ax / ay);
        } else {
            ay = y;
            result = ax / ay;
        }
    }
    if (z) *z = result;
}

i7val glulx_div_r(i7val x, i7val y) {
    i7val z;
    glulx_div(x, y, &z);
    return z;
}

void glulx_mod(i7val x, i7val y, i7val *z) {
    if (y == 0) { printf("Division of %d by 0\n", x); if (z) *z = 0; return; }
    int result, ax, ay;
    if (y < 0) {
        ay = -y;
    } else {
        ay = y;
    }
    if (x < 0) {
        ax = (-x);
        result = -(ax % ay);
    } else {
        ax = x;
        result = ax % ay;
    }
    if (z) *z = result;
}

i7val glulx_mod_r(i7val x, i7val y) {
    i7val z;
    glulx_mod(x, y, &z);
    return z;
}

typedef float gfloat32;

i7val encode_float(gfloat32 val) {
    i7val res;
    *(gfloat32 *)(&res) = val;
    return res;
}

gfloat32 decode_float(i7val val) {
    gfloat32 res;
    *(i7val *)(&res) = val;
    return res;
}

void glulx_exp(i7val x, i7val *y) {
    *y = encode_float(expf(decode_float(x)));
}

void glulx_fadd(i7val x, i7val y, i7val *z) {
    *z = encode_float(decode_float(x) + decode_float(y));
}

void glulx_fdiv(i7val x, i7val y, i7val *z) {
    *z = encode_float(decode_float(x) / decode_float(y));
}

void glulx_floor(i7val x, i7val *y) {
    *y = encode_float(floorf(decode_float(x)));
}

void glulx_fmod(i7val x, i7val y, i7val *z, i7val *w) {
    float fx = decode_float(x);
    float fy = decode_float(y);
    float fquot = fmodf(fx, fy);
    i7val quot = encode_float(fquot);
    i7val rem = encode_float((fx-fquot) / fy);
    if (rem == 0x0 || rem == 0x80000000) {
        /* When the quotient is zero, the sign has been lost in the
         shuffle. We'll set that by hand, based on the original
         arguments. */
        rem = (x ^ y) & 0x80000000;
    }
    if (z) *z = quot;
    if (w) *w = rem;
}

void glulx_fmul(i7val x, i7val y, i7val *z) {
    *z = encode_float(decode_float(x) * decode_float(y));
}

void glulx_fsub(i7val x, i7val y, i7val *z) {
    *z = encode_float(decode_float(x) - decode_float(y));
}

void glulx_ftonumn(i7val x, i7val *y) {
    float fx = decode_float(x);
    i7val result;
    if (!signbit(fx)) {
        if (isnan(fx) || isinf(fx) || (fx > 2147483647.0))
            result = 0x7FFFFFFF;
        else
            result = (i7val) (roundf(fx));
    }
    else {
        if (isnan(fx) || isinf(fx) || (fx < -2147483647.0))
            result = 0x80000000;
        else
            result = (i7val) (roundf(fx));
    }
    *y = result;
}

void glulx_ftonumz(i7val x, i7val *y) {
    float fx = decode_float(x);
    i7val result;
    if (!signbit(fx)) {
        if (isnan(fx) || isinf(fx) || (fx > 2147483647.0))
            result = 0x7FFFFFFF;
        else
            result = (i7val) (truncf(fx));
    }
    else {
        if (isnan(fx) || isinf(fx) || (fx < -2147483647.0))
            result = 0x80000000;
        else
            result = (i7val) (truncf(fx));
    }
    *y = result;
}

void glulx_numtof(i7val x, i7val *y) {
    *y = encode_float((float) x);
}

int glulx_jfeq(i7val x, i7val y, i7val z) {
    int result;
    if ((z & 0x7F800000) == 0x7F800000 && (z & 0x007FFFFF) != 0) {
        /* The delta is NaN, which can never match. */
        result = 0;
    } else if ((x == 0x7F800000 || x == 0xFF800000)
            && (y == 0x7F800000 || y == 0xFF800000)) {
        /* Both are infinite. Opposite infinities are never equal,
        even if the difference is infinite, so this is easy. */
        result = (x == y);
    } else {
        float fx = decode_float(y) - decode_float(x);
        float fy = fabs(decode_float(z));
        result = (fx <= fy && fx >= -fy);
    }
    if (!result) return 1;
    return 0;
}

int glulx_jfne(i7val x, i7val y, i7val z) {
    int result;
    if ((z & 0x7F800000) == 0x7F800000 && (z & 0x007FFFFF) != 0) {
        /* The delta is NaN, which can never match. */
        result = 0;
    } else if ((x == 0x7F800000 || x == 0xFF800000)
            && (y == 0x7F800000 || y == 0xFF800000)) {
        /* Both are infinite. Opposite infinities are never equal,
        even if the difference is infinite, so this is easy. */
        result = (x == y);
    } else {
        float fx = decode_float(y) - decode_float(x);
        float fy = fabs(decode_float(z));
        result = (fx <= fy && fx >= -fy);
    }
    if (!result) return 1;
    return 0;
}

int glulx_jfge(i7val x, i7val y) {
    if (isgreaterequal(decode_float(x), decode_float(y))) return 1;
    return 0;
}

int glulx_jflt(i7val x, i7val y) {
    if (isless(decode_float(x), decode_float(y))) return 1;
    return 0;
}

int glulx_jisinf(i7val x) {
    if (isinf(decode_float(x))) return 1;
    return 0;
}

int glulx_jisnan(i7val x) {
    if (isnan(decode_float(x))) return 1;
    return 0;
}

void glulx_log(i7val x, i7val *y) {
    *y = encode_float(logf(decode_float(x)));
}

void glulx_acos(i7val x, i7val *y) {
    *y = encode_float(acosf(decode_float(x)));
}

void glulx_asin(i7val x, i7val *y) {
    *y = encode_float(asinf(decode_float(x)));
}

void glulx_atan(i7val x, i7val *y) {
    *y = encode_float(atanf(decode_float(x)));
}

void glulx_ceil(i7val x, i7val *y) {
    *y = encode_float(ceilf(decode_float(x)));
}

void glulx_cos(i7val x, i7val *y) {
    *y = encode_float(cosf(decode_float(x)));
}

void glulx_pow(i7val x, i7val y, i7val *z) {
    if (decode_float(x) == 1.0f)
        *z = encode_float(1.0f);
    else if ((decode_float(y) == 0.0f) || (decode_float(y) == -0.0f))
        *z = encode_float(1.0f);
    else if ((decode_float(x) == -1.0f) && isinf(decode_float(y)))
        *z = encode_float(1.0f);
    else
        *z = encode_float(powf(decode_float(x), decode_float(y)));
}

void glulx_sin(i7val x, i7val *y) {
    *y = encode_float(sinf(decode_float(x)));
}

void glulx_sqrt(i7val x, i7val *y) {
    *y = encode_float(sqrtf(decode_float(x)));
}

void glulx_tan(i7val x, i7val *y) {
    *y = encode_float(tanf(decode_float(x)));
}