diff --git a/tests/browser/compiler-explorer.test.js b/tests/browser/compiler-explorer.test.js index fc409b2c..644ccd55 100644 --- a/tests/browser/compiler-explorer.test.js +++ b/tests/browser/compiler-explorer.test.js @@ -354,6 +354,83 @@ const noJava = : "javac/java are not available"; describe("toolchain harnesses", { concurrency: true }, () => { + for (const value of ["INFINITY", "-INFINITY", "NAN"]) { + it( + `C double harness preserves all cases when returning ${value}`, + { skip: noCc }, + async () => { + const dir = mkdtempSync(join(tmpdir(), "codetrial-c-double-")); + try { + const source = join(dir, "growth.c"); + const binary = join(dir, "growth"); + writeFileSync( + source, + generateHarness( + "c", + judges["powx-n"], + `double growthOverPeriods(double x, int n) { + if (n < 0) return ${value}; + double result = 1.0; + for (int i = 0; i < n; i++) result *= x; + return result; +}`, + ), + ); + await run("cc", ["-std=c17", source, "-o", binary]); + const parsed = parseCompilerResults((await run(binary)).stdout); + assert.equal(parsed.setupError, undefined); + assert.deepEqual( + parsed.results.map((result) => result.actual), + [1024, 9.261000000000001, null, 1, null, null], + ); + } finally { + rmSync(dir, { recursive: true, force: true }); + } + }, + ); + } + + it( + "C double array harness preserves finite elements beside non-finite values", + { skip: noCc }, + async () => { + const dir = mkdtempSync(join(tmpdir(), "codetrial-c-double-array-")); + try { + const source = join(dir, "array.c"); + const binary = join(dir, "array"); + const spec = { + kind: "function", + entry: "numbers", + paramNames: [], + paramTypes: [], + returnType: "double[]", + cases: [{ input: [], expected: [1.25, null, null, null, 2.5] }], + }; + writeFileSync( + source, + generateHarness( + "c", + spec, + `double* numbers(int* returnSize) { + static double values[] = {1.25, INFINITY, -INFINITY, NAN, 2.5}; + *returnSize = 5; + return values; +}`, + ), + ); + await run("cc", ["-std=c17", source, "-o", binary]); + const parsed = parseCompilerResults((await run(binary)).stdout); + assert.equal(parsed.setupError, undefined); + assert.deepEqual( + parsed.results.map((result) => result.actual), + [[1.25, null, null, null, 2.5]], + ); + } finally { + rmSync(dir, { recursive: true, force: true }); + } + }, + ); + it( "C Two Sum harness can produce passing Compiler Explorer JSON", { skip: noCc }, diff --git a/web/compiler-explorer.js b/web/compiler-explorer.js index bd462ee2..fe8c8ebd 100644 --- a/web/compiler-explorer.js +++ b/web/compiler-explorer.js @@ -392,7 +392,8 @@ static struct Node* randomList(int values[][2], int size) { originalRandomNodesS static struct Node* graphNode(int** adjacency, int size, int* colSize) { originalGraphNodesSize = size; if (!size) return NULL; struct Node** nodes = calloc(size, sizeof(*nodes)); for (int i = 0; i < size; i++) { nodes[i] = calloc(1, sizeof(*nodes[i])); nodes[i]->val = i + 1; nodes[i]->numNeighbors = colSize[i]; nodes[i]->neighbors = calloc(colSize[i], sizeof(*nodes[i]->neighbors)); originalGraphNodes[i] = nodes[i]; } for (int i = 0; i < size; i++) for (int j = 0; j < colSize[i]; j++) nodes[i]->neighbors[j] = nodes[adjacency[i][j] - 1]; struct Node* first = nodes[0]; free(nodes); return first; } static struct Node* nextTree(int* values, bool* nulls, int size) { if (!size || nulls[0]) return NULL; struct Node** nodes = calloc(size, sizeof(*nodes)); for (int i = 0; i < size; i++) if (!nulls[i]) { nodes[i] = calloc(1, sizeof(*nodes[i])); nodes[i]->val = values[i]; } for (int i = 0, child = 1; i < size && child < size; i++) { if (!nodes[i]) continue; if (child < size) nodes[i]->left = nodes[child++]; if (child < size) nodes[i]->right = nodes[child++]; } struct Node* root = nodes[0]; free(nodes); return root; } static void json_int_array(FILE* out, const int* values, int size) { fputc('[', out); for (int i = 0; i < size; i++) { if (i) fputc(',', out); fprintf(out, "%d", values[i]); } fputc(']', out); } -static void json_double_array(FILE* out, const double* values, int size) { fputc('[', out); for (int i = 0; i < size; i++) { if (i) fputc(',', out); fprintf(out, "%.17g", values[i]); } fputc(']', out); } +static void json_double(FILE* out, double value) { if (isfinite(value)) fprintf(out, "%.17g", value); else fputs("null", out); } +static void json_double_array(FILE* out, const double* values, int size) { fputc('[', out); for (int i = 0; i < size; i++) { if (i) fputc(',', out); json_double(out, values[i]); } fputc(']', out); } static void json_string_array(FILE* out, char** values, int size) { fputc('[', out); for (int i = 0; i < size; i++) { if (i) fputc(',', out); json_string(out, values[i]); } fputc(']', out); } static void json_int_matrix(FILE* out, int** values, int size, int* colSize) { fputc('[', out); for (int i = 0; i < size; i++) { if (i) fputc(',', out); json_int_array(out, values[i], colSize[i]); } fputc(']', out); } static void json_char_matrix(FILE* out, char** values, int size, int* colSize) { fputc('[', out); for (int i = 0; i < size; i++) { if (i) fputc(',', out); fputc('[', out); for (int j = 0; j < colSize[i]; j++) { if (j) fputc(',', out); char item[2] = { values[i][j], 0 }; json_string(out, item); } fputc(']', out); } fputc(']', out); } @@ -485,7 +486,7 @@ function cPrintActual(spec) { if (spec.outputType === "quadTree") return `json_quad_tree(stdout, ${name});`; if (type === "boolean") return `printf(${name} ? "true" : "false");`; if (type === "integer") return `printf("%d", ${name});`; - if (type === "double") return `printf("%.17g", ${name});`; + if (type === "double") return `json_double(stdout, ${name});`; if (type === "string") return `json_string(stdout, ${name});`; if (type === "integer[]") return `json_int_array(stdout, ${name}, ${Number.isInteger(spec.outputParam) ? `${name}Size` : "returnSize"});`;