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; }
- This is part of the extract file inform7_clib.h.
§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))); }
- This is part of the extract file inform7_clib.h.