Skip to content

Basic support for floats (f16, f32, f64, f128)#995

Open
dkcumming wants to merge 9 commits intomasterfrom
dc/float-basics
Open

Basic support for floats (f16, f32, f64, f128)#995
dkcumming wants to merge 9 commits intomasterfrom
dc/float-basics

Conversation

@dkcumming
Copy link
Collaborator

This PR adds basic support for IEEE 754 Floating Point Numbers, specifically f16, f32, f64, f128.

These are already supported in K through builtins (see domains.md). This work is derivative of the implementation in the c-semantics (see ieee754.k in that repository).

In particular:

  • 6 tests are added that tests each type for equality, negation, comparison, casting, arithmetic, and special values (NaN and Infinity)
  • Documentation and helpers for floats are added to numbers.md
  • Decoding is added for floats from bytes in numbers.md (see note on haskell backend below)
  • Semantic rules for arithmetic are extended for floats in data.md

Haskell Backend

The haskell backend has no Float builtins (no Float.hs in kore/src/Kore/Builtin/). This means kore-exec crashes with "missing hook FLOAT.int2float" when attempting to evaluate a float. The booster avoids this by delegating Float evaluation to the LLVM shared library via simplifyTerm in booster/library/Booster/LLVM.hs.

Prior to being able to decode floats, they were left as UnableToDecode which did not throw and error in the exec-smir test suite for --haskell-backend. Now that they are able to be decoded, the haskell backend throws on these decoded values. So I am now skipping any tests with decoded floats for exec-smir with haskell backend.

Mirror of existing IntTy rules
Heavily inspired from c-semantics ieee754.md
The haskell backend does not natively support floats. So the exec-smir
tests that use the haskell backend will fail now that the floats can be
decoded. This is not a problem for the LLVM backend, nor the booster as
it will call the LLVM backend to evaulate the float terms.

The solution is to skip tests that have floats and the haskell backend
for exec-smir (with an explanatory comment for anyone to follow).
Copy link
Collaborator

@mariaKt mariaKt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have left some questions and some comments. Feel free to address them with other PRs, so as not to block you.

FLOATTY
)

syntax Value ::= #decodeFloatParts ( sign: Int, biasedExp: Int, storedSig: Int, FloatTy ) [function]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not explicitly marked as total, however it would need to be (because #decodeFloatRaw is). It seems that the arguments' match is exhaustive. Consider making it total?


rule #reconstructFloat(SIG, AEXP, FLOATTY)
=> roundFloat(
Int2Float(SIG, 256, 64) /Float Int2Float(1 <<Int (0 -Int AEXP), 256, 64),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand the conversion here, but is seems different from the approach in the c-semantics. This conversion has an additional rounding step. I wonder if is intentional (avoid the Rat sort and Rat2Float for some reason?). If not, the following rule should work equally well with only one rounding, and no arbitrary precision. (imports RAT is needed)

     rule #reconstructFloat(SIG, AEXP, FLOATTY)
       => Rat2Float(SIG /Rat (1 <<Int (0 -Int AEXP)),
            #significandBits(FLOATTY), #exponentBits(FLOATTY))
       requires AEXP <Int 0

syntax Int ::= cmpFloat ( Float, Float ) [function]
rule cmpFloat(VAL1, VAL2) => -1 requires VAL1 <Float VAL2
rule cmpFloat(VAL1, VAL2) => 0 requires VAL1 ==Float VAL2
rule cmpFloat(VAL1, VAL2) => 1 requires VAL1 >Float VAL2
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How are the comparisons with NaN handled? How do they not get stuck with some of the tests' values where NaN and another float reach here? Do they not?

rule onFloat(binOpMul, X, Y) => X *Float Y [preserves-definedness]
rule onFloat(binOpMulUnchecked, X, Y) => X *Float Y [preserves-definedness]
rule onFloat(binOpDiv, X, Y) => X /Float Y [preserves-definedness]
rule onFloat(binOpRem, X, Y) => X %Float Y [preserves-definedness]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

%Float implements the % operation according to the IEEE 754 standard:
"IEEE 754 Remainder: The result r=x-y*n, where n is the integer nearest to the exact value x/y"

    rule onFloat(binOpRem, X, Y)
       => X -Float Y *Float truncFloat(X /Float Y)

@mariaKt
Copy link
Collaborator

mariaKt commented Mar 18, 2026

A test for % could be included as well.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants