Skip to content

Future: switch Float SMT encoding from Real to FPSort (IEEE-754) #300

Description

@alcides

Future work: switch Float SMT encoding from Real to FPSort

Today Float is encoded as z3 Real. This keeps SMT reasoning decidable, sort-uniform and rounding-mode-free, but it does not match runtime IEEE-754 semantics (rounding, overflow, NaN, infinities). This issue tracks what it would take to move to a true floating-point encoding (FPSort), and what we would gain. It is intentionally left for the future — it is a design change, not a one-line sort swap. See the analysis comments below for the empirical evidence (verified against z3 4.15.3) of why a naive swap fails.

Current state (entry points)

  • get_sort: Float -> z3.RealSort() — aeon/verification/smt.py:554-555
  • make_variable: Float vars -> Real(name) — aeon/verification/smt.py:631
  • base_functions: all numeric operators (+ - * / == != < <= > >=) are generic Python operators shared between Int and Float — aeon/verification/smt.py:116-138

The Real encoding works precisely because that one generic, sort-agnostic operator table can serve both Int and Float.

What needs to be done

  1. Introduce explicit float widths. There is no single "Float" in FP: results depend on precision (0.1 + 0.2 == 0.3 is unsat at FP64, sat over Real). A fixed FPSort(8, 24) bakes a representation choice into the type system, so this likely means a Float32/Float64 type family rather than one Float, with FPSort(8, 24) / FPSort(11, 53) respectively.
  2. Make operator translation sort-aware. FP sorts do not mix with the Int/Real literals we emit (FP + IntVal(1) and FP + RealVal(1.5) both raise sort mismatch). translate_app / base_functions can no longer be type-agnostic: each operator must dispatch on operand sort and insert coercions (fpToReal, fpToFP, ToReal, fpToInt, …) for mixed Int/Float refinements.
  3. Thread a rounding mode through arithmetic. FP + - * / require an explicit rounding mode (fpAdd(RNE(), x, y), not fpAdd(x, y)). Pick and thread a policy (default roundNearestTiesToEven), and decide whether it is user-controllable.
  4. Handle NaN / infinities in comparisons. Over FP, x == x is not a tautology (NaN) and x + 1.0 > x can fail (infinities/saturation). Equality and ordering need FP-aware predicates (fpEQ, fpLT, …) and the type system must decide how NaN/Inf interact with refinements (e.g. exclude NaN by default, or expose isNaN/isInfinite).
  5. Float literals. Emit FPVal(v, sort) instead of RealVal, and route literal typing through the chosen width.
  6. Migrate / re-validate existing specs. Algebraic specs that verify today under Real (e.g. def half (x : Float) : {y:Float | y * 2.0 == x} = x / 2.0;) may no longer be valid under FP. Existing Float refinements and tests in tests/smt_test.py must be reviewed.

What would be gained

  • Faithful runtime semantics: verification would reason about the same rounding/overflow/NaN behaviour that the evaluator actually produces, closing the gap where a refinement is proven over Real but violated at runtime by rounding.
  • Ability to express and check FP-specific properties: NaN-freedom, absence of overflow/underflow, bounds that account for representable values, etc.
  • Correctness for numerically sensitive code (the kind where 0.1 + 0.2 != 0.3 matters).

Trade-off to keep in mind

We would lose the exact, decidable, sort-uniform, rounding-free arithmetic that makes the current generic translation simple and makes clean algebraic refinements valid. FP solving is also generally harder for the SMT backend. A reasonable path is to keep Real as the default and add FP behind an explicit opt-in fixed-width float type, only where IEEE-754-accurate refinements are actually needed.


Note: the previous inline TODO/commented-out FPSort(8, 24) stub in make_variable was removed in #314; this issue is now the canonical record of the plan.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    wontfixThis will not be worked on

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions