feat: add Float32 primitive type with arithmetics and conversions to Float#5906
Merged
Conversation
Documents all files to change for parsing, type-checking, IR lowering, value representation, Candid binding, and prim.mo additions. No codegen changes in scope. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- type.ml/mli: add Float32 prim variant (tag 19), string mapping, span, subtype - arrange_type.ml, typ_hash.ml: handle Float32 in exhaustive matches - numerics.ml/mli: Float32 module backed by Wasm.F64 - value.ml/mli: Float32 value variant + as_float32 accessor - show.ml: display Float32 values; coverage.ml: Float32 → Any - prim.ml: Float↔Float32 conversions via num_conv_trap_prim - mo_to_idl.ml: Float32 → Candid float32 - idl_to_mo.ml: Candid float32 → Motoko Float32 (was UnsupportedCandidFeature) - prelude.mo: type Float32 = prim "Float32" (global scope) - prim.mo: public type + floatToFloat32/float32ToFloat primitives - test/run/float32.mo: type-check + IR interpreter tests pass Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- prim.ml: floatToFloat32 now truncates through a real 32-bit float (Int32.float_of_bits ∘ Int32.bits_of_float) to purge excess f64 precision - error_codes.ml: retire M0161 as DEFUNCT (was 'Candid float32 cannot be imported'), consistent with other defunct codes - test/run/float32.mo: add precision assertions — verify 0.1 round-trip differs from original and that two f32 round-trips are idempotent Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…freedom "f32" violated the prefix-free invariant since "f" (Float) is a prefix of "f32". Use "h" (half the width of Float/f64) instead — no existing hash starts with "h". Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Prim module now exports Float32 type and floatToFloat32/float32ToFloat, so all snapshots that dump the full prim scope need updating. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add Float32->Text prim, @text_of_Float32 in internals, and ir_passes/show.ml case; test demonstrates '1.5f' output. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- to_idl_prim: Float32 -> Some 13l (Candid float32) - SR.of_type / stackrep_of_type: Float32 -> UnboxedFloat64 - ReadBuf.read_float32: load f32, promote to f64, advance 4 bytes - Serialization: demote f64->f32, store 4 bytes - Deserialization: read_float32 + Float.box - NumConvTrapPrim Float->Float32: demote+promote round-trip - NumConvTrapPrim Float32->Float: identity (already UnboxedFloat64) - @text_of_Float32: implemented via @text_of_Float + "f" suffix (avoids a Float32->Text OtherPrim in both codegen backends) - Remove //SKIP comp from float32.mo; accept wasm-run output Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When UnboxedFloat32 lands, @text_of_Float32 will need to handle an f32 on the stack directly; delegating via num_conv_Float32_Float would still work but is awkward. Keep Float32->Text as a proper OtherPrim in both backends (currently reads UnboxedFloat64, appends "f" suffix), ready for the TODO upgrade. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Consistent with Nat8 showing as bare '42', not '42n8'. The 'f' suffix belongs in AST/IR dumps, not debug_show. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add `Float32Lit of Numerics.Float32.t` to the AST (syntax.ml) and IR (ir.ml), analogous to `Nat8Lit`, so that `(3.14 : Float32)` is properly coerced (demote f64→f32 at compile time) rather than desugaring through floatToFloat32. Also fixes `buffer_size` (4 bytes) and `compile_eq` for Float32 in both backends, enabling `to_candid`/`from_candid` roundtrips. Adds drun test `idl-float32.mo` with echo, literal, and Float32→Float64 queries. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add Float32 to sign_unop, num_binop, num_relop in operator.ml so that ==, !=, <, >, <=, >= and arithmetic work on Float32 in the interpreter; arithmetic results are demoted to f32 precision - Extract the f32-truncation into Numerics.Float32.demote (replaces the inline Int32.(float_of_bits (bits_of_float ...)) in prim.ml and typing.ml) - Fixes 4 remaining nix build .#tests.candid failures (Float32 == failing with M0060) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Tests Candid serialization of Float32 literals (3.14, 1.5) and verifies that a Float64 blob yields null when decoded as Float32. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…and add Changelog entry Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…pported) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Contributor
Contributor
|
Good use of AI to do something tedious! |
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> test: switch float32.mo to -dl to show Float32Lit in IR dump Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> test: filter float32 IR dump to Float32Lit lines only Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> test: remove stale float32.tc.ok
ggreif
commented
Mar 13, 2026
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Contributor
Author
If you have a moment available, can you verify that 0abf97e (+revs) is sane? |
Replace per-pattern T.(...) local opens with a single let open T in at the top of check_lit. Save the local sub wrapper as is_sub before the open to avoid shadowing by T.sub (which has a different signature). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…just Add Const.Float32 to the Const.lit type (mirroring compile_classical.ml), emit it directly from Float32Lit, and handle it cleanly in build_constant_aux and StackRep.adjust — replacing the two ad-hoc cases (Vanilla extraction and Float64 demotion) with a single Const.Float32 f, UnboxedFloat32 case. Also fix check_lit in typing.ml: save shadowed `error` as `error'` before `let open T in` to avoid T.error (a typ value) taking over. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…T in Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Following the pattern of compile_comparison_f64, extract a compile_comparison_f32 helper for Float32 relops in both compile_enhanced.ml and compile_classical.ml. Also add compile_comparison_f64 helper to compile_classical.ml for symmetry. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add `let open Type in` after `let open Operator in` in compile_relop in both backends, removing repeated `Type.(Prim ...)` patterns. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
crusso
reviewed
Mar 27, 2026
crusso
reviewed
Mar 27, 2026
| | Const.Lit (Const.Float64 number) -> Float.constant env number | ||
| | Const.Lit (Const.Float32 f) -> | ||
| E.Vanilla Int64.(logor | ||
| (shift_left (logand (of_int32 (Wasm.F32.to_bits f)) 0xFFFFFFFFL) 32) |
Contributor
There was a problem hiding this comment.
Isn't the logand redundant?
Contributor
Author
There was a problem hiding this comment.
Absolutely! I even let Claude correct such an instance in the past :-)
Contributor
Author
There was a problem hiding this comment.
Indeed redundant — shift_left _ 32 shifts the sign-extended upper 32 bits completely out. Removed.
crusso
reviewed
Mar 27, 2026
Comment on lines
+11649
to
+11650
| (* Never-heap-boxed types (UnboxedWord64 / UnboxedFloat32) have bit 0 = 0 | ||
| in their Vanilla encoding, so Opt.inject is a no-op — emit directly. *) |
Contributor
There was a problem hiding this comment.
I guess you can delete the comment or move to Opt.inject
- Drop redundant `logand 0xFFFFFFFFL` in Float32 constant builder: `shift_left _ 32` already discards the sign-extended upper bits - Remove now-stale OptPrim comment (explained Opt.inject internals) - Delete .claude/plans/ — all implemented, outdated or deferred Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
crusso
reviewed
Mar 27, 2026
Contributor
crusso
left a comment
There was a problem hiding this comment.
LGTM apart from three comments
crusso
approved these changes
Mar 27, 2026
rvanasa
added a commit
to caffeinelabs/vscode-motoko
that referenced
this pull request
Mar 27, 2026
rvanasa
added a commit
to caffeinelabs/node-motoko
that referenced
this pull request
Mar 27, 2026
4 tasks
This was referenced Mar 27, 2026
github-merge-queue Bot
pushed a commit
that referenced
this pull request
Apr 2, 2026
…#5947) (Cherrypicked reconstruction of #5939 after base was squash merged) Builds on the Opt.inject optimization in #5906, but extends it to more payload types. Implements #5938. * less code * more perf same correctness? Saves 10%(!) of instructions on bench/alloc (creating a large linked list, so not surprising) Background on our option representation: https://www.joachim-breitner.de/blog/787-A_mostly_allocation-free_optional_type - [x] Consider promoting, not just normalizing, to exploit bounds that are better than Any. - [x] Tests... Many iterators should get faster too. - [ ] Should we load_forwarding_pointer in fast path or not. If so, need to special case (or slow-path) bools coz of true literal looks like a pointer but can't be forwarded. For some reason, the existing Opt.simple_inject (a no-op) always forwards (but is only every used on reference types). - [x] Optimize Opt.project too. --------- Co-authored-by: Cycle and memory benchmark updater <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Gabor Greif <gabor.greif@caffeine.ai>
This was referenced May 6, 2026
pull Bot
pushed a commit
to mikeyhodl/motoko
that referenced
this pull request
May 6, 2026
## Summary - Adds `e(Float32) = float32` and `i(float32) = Float32` to the type mapping tables. - Removes the note that said importing `float32` was unsupported/failing. ## Why `Float32` support was shipped in caffeinelabs#5906, which updated both `idl_to_mo.ml` and `mo_to_idl.ml` to handle the `float32 ↔ Float32` mapping. The spec doc (`design/IDL-Motoko.md`) was not updated at the time, leaving it contradicting the implementation. This is a companion to a fix in [dfinity/candid](https://github.com/dfinity/candid/pull/new/fix/motoko-binding-float32) where `didc bind --target mo` panics on any `.did` file containing `float32`, because it (correctly) follows the now-outdated spec. Made with [Cursor](https://cursor.com) Co-authored-by: Cursor <cursoragent@cursor.com>
Kamirus
added a commit
to dfinity/candid
that referenced
this pull request
May 7, 2026
…ding (#728) ## Summary - Replace `panic!("float32 not supported in Motoko")` with `str("Float32")` in the Motoko binding generator. - Add a `float.did` test fixture covering `float32`/`float64` round-trip methods. ## Why Motoko added `Float32` in version 1.4.0 ([caffeinelabs/motoko#5906](caffeinelabs/motoko#5906)), updating both the import and export IDL mappings. The spec (`design/IDL-Motoko.md`) is being updated in a companion PR. This brings `didc bind --target mo` in line with what `moc` already does when importing `.did` files directly. Concretely, any `.did` file using `float32` (e.g. the IC management canister interface) would previously cause `didc bind --target mo` to panic at runtime. --------- Co-authored-by: Cursor <cursoragent@cursor.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implement some
Float32functionality (basic arithmetics). This is experimental for now and somewhat starved in terms of supporting functions in the prelude (though basic arithmetic is there, everything else must pass viaFloatif missing, e.g. primitives likesin). But it should be efficient memory consumption-wise.Can receive and send
float32by Candid messages. This was the main motivator for the feature. Stable variables ofFloat32type work too.TODO:
safeFloatToFloat32 : Float -> Epsilon -> ?Float32.mostprocess working?Float32idea, and the unboxedFloat– analogously on 64-bit backend!)