Skip to content

Fix HalfTypeHelper for Infinity and NaN - #613

Open
7aGiven wants to merge 3 commits into
FNA-XNA:masterfrom
7aGiven:HalfTypeHelper
Open

Fix HalfTypeHelper for Infinity and NaN#613
7aGiven wants to merge 3 commits into
FNA-XNA:masterfrom
7aGiven:HalfTypeHelper

Conversation

@7aGiven

@7aGiven 7aGiven commented Jul 21, 2026

Copy link
Copy Markdown
Contributor
  • I confirm that I am the author of this code and release it to the FNA project under the Ms-PL license. This contribution does not contain code from other sources, including code generated by a Large Language Model ("AI").

The same behavior as XNA.

Test case:

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics.PackedVector;
using System;

namespace ConsoleApp4
{
    internal class testfile
    {
        [STAThread]
        private static unsafe void Main4(string[] args)
        {
            //uint m = 1;
            //short e = 17;
            //uint sign = 0;
            //uint fi = (uint)(sign << 31 ^ e + 127 << 23 ^ m);
            //float f1 = *(float*)&fi;

            float f1 = float.PositiveInfinity;

            uint i1 = *(uint*)&f1;
            HalfSingle half = new HalfSingle(f1);

            float f2 = half.ToSingle();
            uint i2 = *(uint*)&f2;

            Console.Error.WriteLine("32bit:" + i1.ToString("X") + ",16bit:" + half.PackedValue.ToString("X") + ",re 32bit:" + i2.ToString("X"));
        }
}

@flibitijibibo

Copy link
Copy Markdown
Member

The removal of uif makes sense, but it'd be good to have an explanation of what range of values changed - the test app just needs more samples, that's all!

@flibitijibibo flibitijibibo self-assigned this Jul 21, 2026
@7aGiven

7aGiven commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

For float s e m.

My e range is [-127, 128]

The change range is:

  1. e > 16
  2. e==16
    The two will branch different.

@7aGiven

7aGiven commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

My change mean:
After rounding 32bit float to 16bit float, e may +1. If e > 16 now, 16 bit float should be (sign << 15 | 0x7FFF)

Note: sign should be 0 or 1.

previous code is not correct.

@7aGiven

7aGiven commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

Previous code with if branch float.Infinity.

e=128 when Infinity.

In fact, when e>16, all is the same regardless float is Infinity.

@7aGiven

7aGiven commented Jul 24, 2026

Copy link
Copy Markdown
Contributor Author

I know what happen now.
HalfSingle is non infinity and NaN.

32bit float to 16bit float:

float f = 5f;
// rounding 23bit mantissa to 10bit mantissa
if (f > HalfSingle.MaxValue)
{
    return HalfSingle.MaxValue;
}
else if (f < HalfSingle.MinValue)
{
    return HalfSingle.MinValue;
}

HalfSingle.MaxValue = 0x7FFF; // s=0, e=16, m=0x3FFF
HalfSingle.MinValue = 0xFFFF; // s=1, e=-15, m=0x3FFF

@7aGiven

7aGiven commented Jul 24, 2026

Copy link
Copy Markdown
Contributor Author

In the transform, 32bit float should be regarded as no NaN and Infinity too.

@7aGiven

7aGiven commented Jul 24, 2026

Copy link
Copy Markdown
Contributor Author

Test case:

using Microsoft.Xna.Framework.Graphics.PackedVector;
using System;

namespace ConsoleApp5
{
    internal static class Show
    {
        [STAThread]
        private static void Main(string[] args)
        {
            Console.WriteLine("PositiveInfinity f32->f16: 0x" + new HalfSingle(float.PositiveInfinity).PackedValue.ToString("X"));
            Console.WriteLine("PositiveInfinity f16->f32: " + F16(0, 16, 0)/*HalfSingle.PositiveInfinity*/.ToSingle());

            Console.WriteLine("NaN f32->f16: 0x" + new HalfSingle(float.NaN).PackedValue.ToString("X"));
            Console.WriteLine("NaN f16->f32: " + F16(1, 16, 0x200)/*HalfSingle.NaN*/.ToSingle());
        }
        // e=[-127, 128] m=[0, 0x7FFFFF]
        private static unsafe float F32(int s, int e, int m)
        {
            int i = s << 31 ^ e + 127 << 23 ^ m;
            return *(float*)&i;
        }
        // e=[-15, 16] m=[0, 0x3FF]
        private static HalfSingle F16(int s, int e, int m)
        {
            HalfSingle hf = default;
            hf.PackedValue = (ushort)(s << 15 ^ e + 15 << 10 ^ m);
            return hf;
        }
    }
}

XNA output:

PositiveInfinity f32->f16: 0x7FFF
PositiveInfinity f16->f32: 65536
NaN f32->f16: 0xFFFF
NaN f16->f32: -98304

FNA output:

PositiveInfinity f32->f16: 0x7C00
PositiveInfinity f16->f32: 65536
NaN f32->f16: 0xFE00
NaN f16->f32: -98304

For IEEE, 0x7C00 mean HalfSingle.PositiveInfinity, 0xFE00 mean HalfSingle.NaN.

For IEEE, 0x7FFF mean HalfSingle.NaN, 0xFFFF mean HalfSingle.NaN.

It mean float.PositiveInfinity not map to HalfSingle.PositiveInfinity.

It mean HalfSingle.NaN and HalfSingle.PositiveInfinity transform to a normal float value.

I think that for XNA, Infinity and NaN is meanless. So ignore it.

@7aGiven

7aGiven commented Jul 24, 2026

Copy link
Copy Markdown
Contributor Author

How do you think? @flibitijibibo

No need process infinity and NaN. It is the same behavior as XNA.

The code for f16 to f32 in FNA not process infinity and NaN. Why process them for f32 to f16 in FNA?

@flibitijibibo

Copy link
Copy Markdown
Member

The tests look good to me, so will defer this to someone who's better at math - @thatcosmonaut should be able to check this out when he's back next week.

@flibitijibibo flibitijibibo removed their assignment Jul 31, 2026
@7aGiven 7aGiven changed the title Fix HalfTypeHelper Fix HalfTypeHelper for Infinity and NaN Aug 5, 2026
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