# 31 — hardware float, and a soft fallback float arithmetic was already a host `double` on the interpreter and the microcore, and the arm64 jit and llvm already lowered 601..609 themselves. this stage makes that split explicit and adds the path for a machine with no FPU. the arm64 jit emits the FPU directly: `fadd`, `fsub`, `fmul`, `fdiv`, `fcmp` plus `cset` (`mi` and `ls`, so unordered is not less), `scvtf` and `fcvtzs`. print and parse stay calls, because they are formatting. llvm builds `fadd`/`fsub`/`fmul`/`fdiv`, `fcmp` (`oeq`/`olt`/`ole`), `sitofp` and `fptosi` on a double bitcast to i64. neither backend calls a C helper for those opcodes. the interpreter and the microcore go through `fops.h`. by default that is a `volatile double`, so the C compiler emits the FPU rather than folding the operation: x86_64 `addsd`/`mulsd`/`ucomisd`/`cvtsi2sd`/`cvttsd2si`, AArch64 `fadd`/`fmul`/`fcmp`/`scvtf`/`fcvtzs`, RV64 `fadd.d`/`fmul.d`/`flt.d`/`fcvt.d.l`/`fcvt.l.d` with `-mabi=lp64d`. the virt boards already set the FPU live before any C runs. RISC-V writes `mstatus.FS` to Dirty (`0x6000`). AArch64 clears the FP trap at EL2 and sets `CPACR_EL1.FPEN`. both images are built hard-float (`-march=rv64gc -mabi=lp64d`, `-march=armv8-a`). `-DZY_SOFTFLOAT` replaces those entry points with an integer IEEE binary64. round ties to even. a NaN becomes a canonical quiet NaN; every other finite result matches the host FPU, including signed zero, subnormals and overflow to infinity. the parser scales with the same add/mul/div, so a soft build and a hard build read a literal the same way. there is still no libc. an AArch64 compile of that path with `-mgeneral-regs-only` is free of `fadd` and of `bl` into a helper. which path a build takes: - hosted interpreter, x86_64: hardware, SSE2, via `fops.h` - hosted microcore, x86_64: the same - arm64 jit under qemu-user: native FPU instructions in the translator - llvm backend: native `double` ops - RISC-V virt: FPU enabled in the boot stub, `fadd.d` in the microcore - AArch64 virt: FPU enabled in the boot stub, `fadd` in the microcore - `-DZY_SOFTFLOAT`: integer fallback, selected at build time. the float test line is the same `test.sh` compiles one interpreter that way and checks `tests/float.zc`. the line is unchanged, so the fixpoint is still 100757 words. the suite after this stage was 369 passed, 0 failed. the one new check is that soft-float interpreter. what is still open: a call result, a global, a field, a parameter and a return are not yet known to be floats. `-0.0` is still true in `if`, because the sign bit is set. `o3` still does not lift 600..611. magnitudes past about 1e32 still print as `inf`, and a parsed significand is still at most 15 digits.