Skip to content
This repository was archived by the owner on Apr 28, 2025. It is now read-only.

Introduce hf32! and hf64! macros for hex float support #351

Merged
merged 1 commit into from
Nov 1, 2024

Conversation

tgross35
Copy link
Contributor

Rust does not have any native way to parse hex floats, but they are heavily used in the C algorithms that we derive from. Introduce a const function that can parse these, as well as macros hf32! and hf64! that ensure the string literals get handled at compiler time.

These are currently not used but making everything available now will ease future development.

@tgross35 tgross35 force-pushed the hex-float branch 2 times, most recently from c3175ee to 886e3fa Compare October 31, 2024 08:35
@tgross35
Copy link
Contributor Author

@quaternic since you have already looked at this algorithm some, would you mind reviewing it here? :)

@quaternic
Copy link
Contributor

quaternic commented Oct 31, 2024

On a high level, I have two considerations:

Firstly, I think the parsing code could be streamlined significantly. As one particular idea, parse_any could return something like (M, E): (i128, i32) with numeric value M * 2^E. This is more general, and would separate (parsing an abstract float) from (constructing a value of fN).

Secondly, it should handle inputs that cannot be represented in the target type, alternatives:

  • report an error (preferred)
  • correct rounding

Allowing for both might be ideal, but it's probably not needed for the purposes of this crate. The bug in sincosf was in part due to the compiler accepting const FOO: f32 = /*literal with lots of digits*/. In decimal you need 0.1 to get rounded, but with hex you can always express the rounded value with fewer digits.

@tgross35
Copy link
Contributor Author

tgross35 commented Oct 31, 2024

Thanks for the feedback! I updated with your suggestion for handling excess precision, that could use a look.

I am not quite sure what you have in mind re. returning (i128, i32) because parse_any always needs sig_bits to figure out exponent bounds and do the shifting, so it seems reasonable to just clear the implicit bit and reconstruct the float there rather than reconstructing everywhere it is called. Unless you just mean to factor some of that out to a new function?

(feel free to sketch it out on the playground if you had something in mind - I'm not tied to this implementation, it was just something I could get together quick)

(I have no clue why this would be getting a SIGILL on ppc but I am sure it is unrelated - maybe something with how should_panic is trying to catch unwinds)

@quaternic
Copy link
Contributor

I am not quite sure what you have in mind re. returning (i128, i32) because parse_any always needs sig_bits to figure out exponent bounds and do the shifting, so it seems reasonable to just clear the implicit bit and reconstruct the float there rather than reconstructing everywhere it is called. Unless you just mean to factor some of that out to a new function?

Right, yes, I meant refactoring it to separate parsing from the details of the target representation.

(feel free to sketch it out on the playground if you had something in mind - I'm not tied to this implementation, it was just something I could get together quick)

Alright, I can do that!

@quaternic
Copy link
Contributor

quaternic commented Nov 1, 2024

Why, oh why, did I volunteer to write parsing code. (edit: As a const fn, even!) It certainly wasn't as quick and easy as suggesting such refactoring was. It's not really a sketch anymore, but pretty much a full rewrite of the internals: playground

In functionality, one concrete improvement is support for any integer parts as in 0x123.4p-56. I followed whatever IEEE says for the parsed format, so it accepts some additional variations, such as 0X as the prefix, or no leading zero as in 0x.123p0.

@tgross35 tgross35 force-pushed the hex-float branch 4 times, most recently from d193a78 to d088acc Compare November 1, 2024 10:00
@tgross35
Copy link
Contributor Author

tgross35 commented Nov 1, 2024

Why, oh why, did I volunteer to write parsing code. (edit: As a const fn, even!) It certainly wasn't as quick and easy as suggesting such refactoring was. It's not really a sketch anymore, but pretty much a full rewrite of the internals: playground

Well, thanks for doing it! const fn for when you miss C style array logic... The code is certainly much cleaner than what I hacked together :)

In functionality, one concrete improvement is support for any integer parts as in 0x123.4p-56. I followed whatever IEEE says for the parsed format, so it accepts some additional variations, such as 0X as the prefix, or no leading zero as in 0x.123p0.

That wasn't even a goal since C never produces those, above and beyond!

I had to massage a few things but I basically picked up exactly what you had. I'll merge it in a bit unless anything else jumps out to you.

@quaternic
Copy link
Contributor

I had to massage a few things but I basically picked up exactly what you had. I'll merge it in a bit unless anything else jumps out to you.

Thanks for putting it together! Looks about right to me.

Rust does not have any native way to parse hex floats, but they are
heavily used in the C algorithms that we derive from. Introduce a const
function that can parse these, as well as macros `hf32!` and `hf64!`
that ensure the string literals get handled at compiler time.

These are currently not used but making everything available now will
ease future development.

Co-authored-by: quaternic <[email protected]>
@tgross35 tgross35 enabled auto-merge (rebase) November 1, 2024 12:07
@tgross35 tgross35 merged commit 9fa3bf3 into rust-lang:master Nov 1, 2024
29 checks passed
@tgross35 tgross35 deleted the hex-float branch November 1, 2024 12:19
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants