;;;; The evidence for lib/js.ml's integer decisions, run by the survey. ;;;; ;;;; JavaScript has one number type and it is a double. Flan has eight integer ;;;; types and they wrap. Every line below is a place where the two disagree ;;;; unless the backend normalises, and the survey diffs this program's output ;;;; against the LLVM build's byte for byte — so a wrong answer here is a ;;;; DIFFER and not a discussion. ;;;; ;;;; Everything goes through a global rather than a literal, for the reason ;;;; arith.flan gives: a literal operand is exactly what a constant folder ;;;; removes, and a folded program does not contain the code being tested. (defvar i8hi i8 127) (defvar i16hi i16 32767) (defvar i32hi i32 2147483647) (defvar u8hi u8 255) (defvar u16hi u16 65535) (defvar u32hi u32 4294967295) (defvar i64hi i64 9223372036854775807) (defvar u64zero u64 0) (defvar one8 i8 1) (defvar one16 i16 1) (defvar one32 i32 1) (defvar oneu8 u8 1) (defvar oneu16 u16 1) (defvar oneu32 u32 1) (defvar one64 i64 1) (defvar oneu64 u64 1) (defvar big32 i32 123456789) (defvar big64 i64 1234567890123) (defvar three i32 3) (defvar three64 i64 3) (defvar seven i32 7) (defvar negsev i32 -7) (defvar shift i32 33) (defvar shift8 i8 9) (defvar f32one f32 1.0) (defvar f32big f32 16777217.0) (defn main [] i32 ;; ── Wrapping at every width ────────────────────────────────────── ;; The top of the type plus one. A JS number would answer 128, 32768, ;; 2147483648, 256, 65536, 4294967296 and never wrap at all. (println (+ i8hi one8)) (println (+ i16hi one16)) (println (+ i32hi one32)) (println (+ u8hi oneu8)) (println (+ u16hi oneu16)) (println (+ u32hi oneu32)) (println (+ i64hi one64)) (println (- u64zero oneu64)) ; the top of u64, which no literal spells ;; A multiply whose exact product passes 2^53, where a * b in JS is not ;; exact and Math.imul is. 123456789 * 123456789 = 15241578750190521, which ;; is above 2^53, so a double rounds it before the truncation can happen. (println (* big32 big32)) (println (* big64 big64)) ;; ── Truncating division, and the sign of a remainder ───────────── ;; C truncates toward zero and so does JS's %, but (a / b) | 0 is only the ;; same answer below 2^31, which is why the backend uses Math.trunc. (println (/ seven three)) (println (/ negsev three)) (println (% seven three)) (println (% negsev three)) (println (/ big64 three64)) ;; ── Shifts, with the count masked to the operand's width ───────── ;; 33 is not a legal shift count for a 32-bit type, and the checker only ;; rejects a *literal* out of range, so this is the computed case emit.ml ;; masks and this backend has to mask too. JS masks a 32-bit shift itself ;; and has no 8- or 16-bit shift at all, which is why the mask is written ;; out rather than relied on. (println (<< one32 shift)) (println (<< one8 shift8)) (println (>> i32hi one32)) (println (>> (- (i32 0) i32hi) one32)) ; arithmetic: signed stays signed (println (>> u32hi oneu32)) ; logical: unsigned stays unsigned (println (<< one64 (i64 65))) ;; ── Bitwise over an unsigned 32-bit value ──────────────────────── ;; JS bitwise operators produce a *signed* 32-bit result, so every one of ;; these needs the >>> 0 back. (println (bit-and u32hi u32hi)) (println (bit-or u32hi oneu32)) (println (bit-xor u32hi oneu32)) ;; ── f32 is not a double ────────────────────────────────────────── ;; 16777217 is the first integer a float cannot hold, so this prints ;; 16777216 under a real f32 and 16777218 under a double. (println (+ f32big f32one)) (println (/ (f32 1.0) (f32 3.0))) (println (/ (f64 1.0) (f64 3.0))) ;; ── The conversions, both ways ─────────────────────────────────── (println (i8 i32hi)) (println (u8 (- (i32 0) one32))) (println (i32 i64hi)) (println (i64 big32)) (println (u64 i64hi)) (println (f64 i64hi)) (println (i32 (f64 2.9))) (println (i32 (f64 -2.9))) 0)