Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix conversions from subnormal System.Half values #46536

Merged
merged 3 commits into from
Jan 6, 2021

Conversation

tannergooding
Copy link
Member

This resolves #46515.

CC. @pgovind, @ecorm

@ghost
Copy link

ghost commented Jan 4, 2021

Tagging subscribers to this area: @tannergooding, @pgovind
See info in area-owners.md if you want to be subscribed.

Issue Details

This resolves #46515.

CC. @pgovind, @ecorm

Author: tannergooding
Assignees: -
Labels:

area-System.Numerics

Milestone: -

@@ -683,10 +683,10 @@ private static double CreateDoubleNaN(bool sign, ulong significand)
}

private static float CreateSingle(bool sign, byte exp, uint sig)
=> BitConverter.Int32BitsToSingle((int)(((sign ? 1U : 0U) << float.SignShift) | ((uint)exp << float.ExponentShift) | sig));
=> BitConverter.Int32BitsToSingle((int)(((sign ? 1U : 0U) << float.SignShift) + ((uint)exp << float.ExponentShift) + sig));
Copy link
Member Author

Choose a reason for hiding this comment

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

The issue was that this was doing a bitwise or when it should have been using addition.

Copy link
Member Author

Choose a reason for hiding this comment

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

Copy link
Contributor

@pgovind pgovind left a comment

Choose a reason for hiding this comment

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

Ah yup, my bad. LGTM

Copy link
Member

@jeffhandley jeffhandley left a comment

Choose a reason for hiding this comment

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

We should submit this for 5.0 servicing approval. In the servicing template on the backport PR, please include notes for which boundary conditions we were previously testing and which ones we missed.

We will need to backport the test updates too (first), which can be done without approval.

@ecorm
Copy link
Contributor

ecorm commented Jan 4, 2021

I've just uncovered another corner case where .NET's Half does not agree with http://half.sourceforge.net/ . When converting from Single(MinSubnormal/2) to Half (where MinSubnormal/2 is 0x33000000), the Sourceforge version makes it underflow to zero, whereas .NET makes it round up to Half.Epsilon. I'm not sure which is correct. I'm currently checking if @tannergooding 's fix makes both implementations agree.

@ecorm
Copy link
Contributor

ecorm commented Jan 4, 2021

Ah yup, my bad. LGTM

I made the same mistake when I adapted http://half.sourceforge.net/ for my own use. I thought the + was an attempt to optimize a bitwise OR, but it turns out it really needs to perform addition for the rounding logic to work.

@ecorm
Copy link
Contributor

ecorm commented Jan 4, 2021

I'm currently checking if @tannergooding 's fix makes both implementations agree.

Nope, they still disagree for the (Half)(BitConverter.Int32BitsToSingle(0x33000000)) case. Perhaps this should be a separate issue.

@tannergooding
Copy link
Member Author

I've found the issue and a local fix, will push it up with a commit momentarily

@@ -635,6 +635,7 @@ private static ushort RoundPackToHalf(bool sign, short exp, ushort sig)
{
sig = (ushort)ShiftRightJam(sig, -exp);
exp = 0;
roundBits = sig & 0xF;
Copy link
Member Author

Choose a reason for hiding this comment

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

The other issue was that this was missing adjusting the roundBits based on the updated sig.

Copy link
Member Author

Choose a reason for hiding this comment

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

The original implementation was doing the correct thing: https://github.com/ucb-bar/berkeley-softfloat-3/blob/b64af41c3276f97f0e181920400ee056b9c88037/source/s_roundPackToF16.c#L77

I imagine this was accidentally dropped when eliding the unnecessary exception handling logic.

@ecorm
Copy link
Contributor

ecorm commented Jan 4, 2021

I'm not sure which is correct.

I think the Sourceforge implementation was correct for the MinSubnormal/2 case. Ties round to even, so UInt16BitsToHalf(0) being even would be the correct choice, whereas UInt16BitsToHalf(0x0001) would be odd.

Sorry if this all sounds elementary, but I've never needed to deal with the gory inner workings of IEEE floats before. 😄

@ecorm
Copy link
Contributor

ecorm commented Jan 4, 2021

Feel free to borrow (and verify) some of my test vectors from here while you're at it: https://gist.github.com/ecorm/3b5963749752f2cc2f96fb493d29fcba

No attribution necessary (or wanted).

@tannergooding
Copy link
Member Author

/backport to release/5.0

@github-actions
Copy link
Contributor

github-actions bot commented Jan 6, 2021

Started backporting to release/5.0: https://github.com/dotnet/runtime/actions/runs/466823164

@github-actions
Copy link
Contributor

github-actions bot commented Jan 6, 2021

@tannergooding backporting to release/5.0 failed, the patch most likely resulted in conflicts:

$ git am --3way --ignore-whitespace --keep-non-patch changes.patch

Applying: Fix conversions from subnormal System.Half values
error: sha1 information is lacking or useless (src/libraries/System.Private.CoreLib/src/System/Half.cs).
error: could not build fake ancestor
hint: Use 'git am --show-current-patch=diff' to see the failed patch
Patch failed at 0001 Fix conversions from subnormal System.Half values
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".
Error: The process '/usr/bin/git' failed with exit code 128

Please backport manually!

tannergooding added a commit to tannergooding/runtime that referenced this pull request Jan 6, 2021
* Fix conversions from subnormal System.Half values

* Fix conversions from subnormal System.Single/Double to System.Half values

* Fixing the HalfTests parameters
jeffhandley pushed a commit that referenced this pull request Jan 13, 2021
…to .NET 5.0 (#46641)

* Half conversion test cases for subnormal corner cases (#46523)

* Half conversion test cases for subnormal corner cases

* Single/Double->Half rounding corner test cases

* Single/Double->Half conversion subnormal rounding corner test cases

* Fix conversions from subnormal System.Half values (#46536)

* Fix conversions from subnormal System.Half values

* Fix conversions from subnormal System.Single/Double to System.Half values

* Fixing the HalfTests parameters

Co-authored-by: Emile Cormier <ecorm@users.noreply.github.com>
@ghost ghost locked as resolved and limited conversation to collaborators Feb 5, 2021
@tannergooding tannergooding deleted the fix-46515 branch November 11, 2022 15:26
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Half to Double/Float conversion is broken for subnormal values
5 participants