Skip to content

Add integer to f16 conversions - #729

Open
tgross35 wants to merge 2 commits into
rust-lang:mainfrom
tgross35:f16-int-conv
Open

tgross35 wants to merge 2 commits into
rust-lang:mainfrom
tgross35:f16-int-conv

Conversation

@tgross35

@tgross35 tgross35 commented Nov 5, 2024

Copy link
Copy Markdown
Member

These are not present in LLVM's compiler-rt but LLVM does emit them in some cases 1.

@tgross35
tgross35 marked this pull request as draft November 5, 2024 00:34
@tgross35
tgross35 force-pushed the f16-int-conv branch 3 times, most recently from 7230b09 to d2d4f22 Compare November 5, 2024 07:50
tgross35 added a commit to tgross35/compiler-builtins that referenced this pull request Nov 5, 2024
CI in [1] seems to indicate that there are cases where the `f16`
infinite recursion bug ([2], [3]) can make its way into what gets called
during tests, even though this doesn't seem to be the usual case. In
order to make sure that we avoid these completely, just unset
`f16_enabled` on any platforms that have the recursion problem.

This also refactors the `match` statement to be more in line with
`library/std/build.rs`.

[1]: rust-lang#729
[2]: llvm/llvm-project#97981
[3]: rust-lang#651
@tgross35
tgross35 force-pushed the f16-int-conv branch 4 times, most recently from 1a86a8d to f6a5429 Compare November 5, 2024 09:07
@tgross35

tgross35 commented Nov 5, 2024

Copy link
Copy Markdown
Member Author

Includes #730 since PPC crashes without it.

tgross35 added a commit to tgross35/rust that referenced this pull request Jun 3, 2025
CI in [1] seems to indicate that there are cases where the `f16`
infinite recursion bug ([2], [3]) can make its way into what gets called
during tests, even though this doesn't seem to be the usual case. In
order to make sure that we avoid these completely, just unset
`f16_enabled` on any platforms that have the recursion problem.

This also refactors the `match` statement to be more in line with
`library/std/build.rs`.

[1]: rust-lang/compiler-builtins#729
[2]: llvm/llvm-project#97981
[3]: rust-lang/compiler-builtins#651
tautschnig pushed a commit to model-checking/verify-rust-std that referenced this pull request Jun 17, 2025
CI in [1] seems to indicate that there are cases where the `f16`
infinite recursion bug ([2], [3]) can make its way into what gets called
during tests, even though this doesn't seem to be the usual case. In
order to make sure that we avoid these completely, just unset
`f16_enabled` on any platforms that have the recursion problem.

This also refactors the `match` statement to be more in line with
`library/std/build.rs`.

[1]: rust-lang/compiler-builtins#729
[2]: llvm/llvm-project#97981
[3]: rust-lang/compiler-builtins#651
tgross35 added a commit that referenced this pull request Jan 31, 2026
CI in [1] seems to indicate that there are cases where the `f16`
infinite recursion bug ([2], [3]) can make its way into what gets called
during tests, even though this doesn't seem to be the usual case. In
order to make sure that we avoid these completely, just unset
`f16_enabled` on any platforms that have the recursion problem.

This also refactors the `match` statement to be more in line with
`library/std/build.rs`.

[1]: #729
[2]: llvm/llvm-project#97981
[3]: #651
@tgross35
tgross35 force-pushed the f16-int-conv branch 2 times, most recently from d95f341 to 9291a00 Compare February 12, 2026 07:23
@tgross35
tgross35 marked this pull request as ready for review February 12, 2026 07:52
@tgross35

Copy link
Copy Markdown
Member Author

@quaternic would you mind double checking some of my logic here?

With this current implementation, we are getting errors like the following:

thread 'i_to_f::__floatunsihf' (15281862) panicked at builtins-test/tests/conv.rs:103:5:
incorrect rounding by __floatunsihf(2147483648): inf (0x7c00)
f->i bracket: (65504, 4294967295, 0)
errors: (2147418144, 2147483647, 2147483648)

Which is coming from the bit of code here https://github.com/tgross35/compiler-builtins/blob/c462b7b5aa56a2ff3cdbfaf7c19143bbdee17b00/builtins-test/tests/conv.rs#L44-L79. I think the implementation is correct, especially considering the tests against apfloat seem okay. So I think this is an error in the validation algorithm here, which only shows up for f16 because f16::MAX is way closer to 0 (NAN as i*) than to any iN::MAX for N >= 32 (all that we have algorithms for). And that isn't true for any other float type, even f32::MAX as u128 is > u128::MAX / 2.

As I'm typing it out that sounds pretty reasonable, just need to figure out how to patch this error algorithm.

@quaternic

Copy link
Copy Markdown
Contributor

I think the validation logic is broken in that the apfloat-results that overflowed to infinity are converted back to an integer, specifically the maximum for the type, which should be irrelevant for the casting from integer to float.

The overflow threshold is specifically when the exponent after rounding exceeds the maximum. But the rounding itself is done as if the exponent was unbounded. So for f16:

[src/main.rs:4:5] f16::MAX.next_down() as f32 = 65472.0
[src/main.rs:5:5] f16::MAX as f32 = 65504.0
[src/main.rs:6:5] 65519 as f16 as f32 = 65504.0
[src/main.rs:7:5] 65520 as f16 as f32 = inf

65520 as f16 overflows because it would round to 65536.0 if the exponent range allowed.

@tgross35

Copy link
Copy Markdown
Member Author

Isn't that represented correctly? The apfloat bit at https://github.com/tgross35/compiler-builtins/blob/c462b7b5aa56a2ff3cdbfaf7c19143bbdee17b00/builtins-test/tests/conv.rs#L29-L37 should be the same as this:

>> :dep rustc_apfloat
   Compiling rustc_apfloat v0.2.3+llvm-462a31f5a5ab
   Compiling smallvec v1.15.1
   Compiling bitflags v2.10.0
>> use rustc_apfloat::ieee::Half
>> use rustc_apfloat::Float;
>> Half::from_i128(65519)
StatusAnd { status: Status(INEXACT), value: 65504(Normal | +[2047] * 2^15) }
>> Half::from_i128(65519).value.to_bits()
31743
>> Half::from_i128(65520)
StatusAnd { status: Status(OVERFLOW | INEXACT), value: +Inf(Infinity | +[0] * 2^16) }
>> Half::from_i128(65520).value.to_bits()
31744

@quaternic

Copy link
Copy Markdown
Contributor

Lines 48-50 as-cast the f16 values back to integers, which saturates inf to u32::MAX, and NaN to 0, which is where you get the printed line

f->i bracket: (65504, 4294967295, 0)

@tgross35

Copy link
Copy Markdown
Member Author

Ah, by

the apfloat-results that overflowed to infinity

I thought you were referring to the test above that uses rustc_apfloat. It's probably fine to skip the error_plus < error part of the check if <$f_ty>::wrapping_add(f1.to_bits().wrapping_sub(1)) is infinite then, right?

@rustbot

This comment has been minimized.

@tgross35

Copy link
Copy Markdown
Member Author

@quaternic forgot to follow up here, do you have an idea what would be better to write for the test assertion?

@npmccallum

Copy link
Copy Markdown

@tgross35 — I've opened #1261, which builds on your implementation here to get integer-to-f16 conversions landed. It carries your conversion commit with you as Co-authored-by:, proposes an answer to the test-oracle question you raised in June (assert against rustc_apfloat directly instead of the f→i bracket, which saturates inf and misfires for f16), and adds a builtins-test-intrinsics link case so the missing symbols surface as a link failure. CI is green across the full matrix.

It's meant to supersede this PR, but I'm equally happy to fold the changes back into your branch instead if you'd rather keep it here — whichever you prefer. Thanks for the original work.

@beetrees beetrees left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

A couple of questions.

r? me

View changes since this review

Comment on lines +87 to +88
// The entire lower half of `i` will be truncated (masked portion), plus the
// next `EXP_BITS` bits.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Unless I'm missing something, I'm not sure the "(masked portion)" makes sense here, as nowhere is the lower half of i masked. The lower quarter of i_m is masked in the line below. (The code is correct as f16::EXP_BITS <= 8.)

// next `EXP_BITS` bits.
let adj = (i_m >> f16::EXP_BITS | i_m & 0xFF) as u16;
let m = m_adj::<f16>(m_base, adj);
let e = if i == 0 { 0 } else { exp::<u32, f16>(n) - 1 };

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Not something that has to be changed in this PR, but is there any reason why some conversion do if i == 0 at the start of the function and some do it when calculating the exponent?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I'm not sure, looks like that came with 7e5768b. Doing that here rather than the beginning makes it branchless, but I wonder if there's still an advantage to the early return? Would be interesting to benchmark.

@tgross35 tgross35 Sep 19, 2026 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Found https://blog.m-ou.se/floats/#removing-the-last-branch which does specifically mention the branchless bit

@rustbot

rustbot commented Sep 18, 2026

Copy link
Copy Markdown
Collaborator

Error: Failed to set assignee to beetrees: invalid assignee

Note: Only org members with at least the repository "read" role, users with write permissions, or people who have commented on the PR may be assigned.

Please file an issue on GitHub at triagebot if there's a problem with this bot, or reach out on #triagebot on Zulip.

@beetrees beetrees mentioned this pull request Sep 22, 2026
@beetrees

Copy link
Copy Markdown
Contributor

r? me

These are not present in LLVM's `compiler-rt` but LLVM does emit them in
some cases [1].

[1]: rust-lang/rust#132614 (comment)
@rustbot

rustbot commented Sep 29, 2026

Copy link
Copy Markdown
Collaborator

This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

@rustbot

rustbot commented Sep 29, 2026

Copy link
Copy Markdown
Collaborator

⚠️ Warning ⚠️

  • The following commits have merge commits (commits with multiple parents) in your changes. We have a no merge policy so these commits will need to be removed for this pull request to be merged.

    You can start a rebase with the following commands:

    $ # rebase
    $ git pull --rebase https://github.com/rust-lang/compiler-builtins.git main
    $ git push --force-with-lease
    

This branch has not been deployed

No deployments
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.

5 participants