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

Add rounding mode remark for half conversions. #229

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
206 changes: 206 additions & 0 deletions src/Tests/Tests/Shared/TestHalf.cs
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,212 @@ public static void half4_maxvalue()
TestUtils.AreEqual(0x7bff, max.w.value);
}

static void CheckAllComponentsForExpected(uint expected, half actual)
{
TestUtils.AreEqual(expected, actual.value);
}

static void CheckAllComponentsForExpected(uint expected, half2 actual)
{
TestUtils.AreEqual(expected, actual.x.value);
TestUtils.AreEqual(expected, actual.y.value);
}

static void CheckAllComponentsForExpected(uint expected, half3 actual)
{
TestUtils.AreEqual(expected, actual.x.value);
TestUtils.AreEqual(expected, actual.y.value);
TestUtils.AreEqual(expected, actual.z.value);
}

static void CheckAllComponentsForExpected(uint expected, half4 actual)
{
TestUtils.AreEqual(expected, actual.x.value);
TestUtils.AreEqual(expected, actual.y.value);
TestUtils.AreEqual(expected, actual.z.value);
TestUtils.AreEqual(expected, actual.w.value);
}

[TestCompiler]
public static void half_check_rounding()
{
// 1 ulp away from a representable half.
CheckAllComponentsForExpected(0x3c01, new half(1.00097668170928955078f));
CheckAllComponentsForExpected(0x3c01, new half(1.00097644329071044922f));

CheckAllComponentsForExpected(0x3c01, new half(1.00097668170928955078));
CheckAllComponentsForExpected(0x3c01, new half(1.00097644329071044922));

// 1 ulp away from the midpoint between two representable halfs.
CheckAllComponentsForExpected(0x5c00, new half(256.124969482421875f));
CheckAllComponentsForExpected(0x5c01, new half(256.125030517578125f));
CheckAllComponentsForExpected(0x5c01, new half(256.374969482421875f));
CheckAllComponentsForExpected(0x5c02, new half(256.375030517578125f));
CheckAllComponentsForExpected(0xdc00, new half(-256.124969482421875f));
CheckAllComponentsForExpected(0xdc01, new half(-256.125030517578125f));
CheckAllComponentsForExpected(0xdc01, new half(-256.374969482421875f));
CheckAllComponentsForExpected(0xdc02, new half(-256.375030517578125f));

CheckAllComponentsForExpected(0x5c00, new half(256.124969482421875));
CheckAllComponentsForExpected(0x5c01, new half(256.125030517578125));
CheckAllComponentsForExpected(0x5c01, new half(256.374969482421875));
CheckAllComponentsForExpected(0x5c02, new half(256.375030517578125));
CheckAllComponentsForExpected(0xdc00, new half(-256.124969482421875));
CheckAllComponentsForExpected(0xdc01, new half(-256.125030517578125));
CheckAllComponentsForExpected(0xdc01, new half(-256.374969482421875));
CheckAllComponentsForExpected(0xdc02, new half(-256.375030517578125));

// At the midpoint between two representable halfs.
CheckAllComponentsForExpected(0x3c01, new half(1.00048828125f));
CheckAllComponentsForExpected(0x5c01, new half(256.125f));
CheckAllComponentsForExpected(0x5c02, new half(256.375f));
CheckAllComponentsForExpected(0xbc01, new half(-1.00048828125f));
CheckAllComponentsForExpected(0xdc01, new half(-256.125f));
CheckAllComponentsForExpected(0xdc02, new half(-256.375f));

CheckAllComponentsForExpected(0x3c01, new half(1.00048828125));
CheckAllComponentsForExpected(0x5c01, new half(256.125));
CheckAllComponentsForExpected(0x5c02, new half(256.375));
CheckAllComponentsForExpected(0xbc01, new half(-1.00048828125));
CheckAllComponentsForExpected(0xdc01, new half(-256.125));
CheckAllComponentsForExpected(0xdc02, new half(-256.375));
}

[TestCompiler]
public static void half2_check_rounding()
{
// 1 ulp away from a representable half.
CheckAllComponentsForExpected(0x3c01, new half2(1.00097668170928955078f));
CheckAllComponentsForExpected(0x3c01, new half2(1.00097644329071044922f));

CheckAllComponentsForExpected(0x3c01, new half2(1.00097668170928955078));
CheckAllComponentsForExpected(0x3c01, new half2(1.00097644329071044922));

// 1 ulp away from the midpoint between two representable halfs.
CheckAllComponentsForExpected(0x5c00, new half2(256.124969482421875f));
CheckAllComponentsForExpected(0x5c01, new half2(256.125030517578125f));
CheckAllComponentsForExpected(0x5c01, new half2(256.374969482421875f));
CheckAllComponentsForExpected(0x5c02, new half2(256.375030517578125f));
CheckAllComponentsForExpected(0xdc00, new half2(-256.124969482421875f));
CheckAllComponentsForExpected(0xdc01, new half2(-256.125030517578125f));
CheckAllComponentsForExpected(0xdc01, new half2(-256.374969482421875f));
CheckAllComponentsForExpected(0xdc02, new half2(-256.375030517578125f));

CheckAllComponentsForExpected(0x5c00, new half2(256.124969482421875));
CheckAllComponentsForExpected(0x5c01, new half2(256.125030517578125));
CheckAllComponentsForExpected(0x5c01, new half2(256.374969482421875));
CheckAllComponentsForExpected(0x5c02, new half2(256.375030517578125));
CheckAllComponentsForExpected(0xdc00, new half2(-256.124969482421875));
CheckAllComponentsForExpected(0xdc01, new half2(-256.125030517578125));
CheckAllComponentsForExpected(0xdc01, new half2(-256.374969482421875));
CheckAllComponentsForExpected(0xdc02, new half2(-256.375030517578125));

// At the midpoint between two representable halfs.
CheckAllComponentsForExpected(0x3c01, new half2(1.00048828125f));
CheckAllComponentsForExpected(0x5c01, new half2(256.125f));
CheckAllComponentsForExpected(0x5c02, new half2(256.375f));
CheckAllComponentsForExpected(0xbc01, new half2(-1.00048828125f));
CheckAllComponentsForExpected(0xdc01, new half2(-256.125f));
CheckAllComponentsForExpected(0xdc02, new half2(-256.375f));

CheckAllComponentsForExpected(0x3c01, new half2(1.00048828125));
CheckAllComponentsForExpected(0x5c01, new half2(256.125));
CheckAllComponentsForExpected(0x5c02, new half2(256.375));
CheckAllComponentsForExpected(0xbc01, new half2(-1.00048828125));
CheckAllComponentsForExpected(0xdc01, new half2(-256.125));
CheckAllComponentsForExpected(0xdc02, new half2(-256.375));
}

[TestCompiler]
public static void half3_check_rounding()
{
// 1 ulp away from a representable half.
CheckAllComponentsForExpected(0x3c01, new half3(1.00097668170928955078f));
CheckAllComponentsForExpected(0x3c01, new half3(1.00097644329071044922f));

CheckAllComponentsForExpected(0x3c01, new half3(1.00097668170928955078));
CheckAllComponentsForExpected(0x3c01, new half3(1.00097644329071044922));

// 1 ulp away from the midpoint between two representable halfs.
CheckAllComponentsForExpected(0x5c00, new half3(256.124969482421875f));
CheckAllComponentsForExpected(0x5c01, new half3(256.125030517578125f));
CheckAllComponentsForExpected(0x5c01, new half3(256.374969482421875f));
CheckAllComponentsForExpected(0x5c02, new half3(256.375030517578125f));
CheckAllComponentsForExpected(0xdc00, new half3(-256.124969482421875f));
CheckAllComponentsForExpected(0xdc01, new half3(-256.125030517578125f));
CheckAllComponentsForExpected(0xdc01, new half3(-256.374969482421875f));
CheckAllComponentsForExpected(0xdc02, new half3(-256.375030517578125f));

CheckAllComponentsForExpected(0x5c00, new half3(256.124969482421875));
CheckAllComponentsForExpected(0x5c01, new half3(256.125030517578125));
CheckAllComponentsForExpected(0x5c01, new half3(256.374969482421875));
CheckAllComponentsForExpected(0x5c02, new half3(256.375030517578125));
CheckAllComponentsForExpected(0xdc00, new half3(-256.124969482421875));
CheckAllComponentsForExpected(0xdc01, new half3(-256.125030517578125));
CheckAllComponentsForExpected(0xdc01, new half3(-256.374969482421875));
CheckAllComponentsForExpected(0xdc02, new half3(-256.375030517578125));

// At the midpoint between two representable halfs.
CheckAllComponentsForExpected(0x3c01, new half3(1.00048828125f));
CheckAllComponentsForExpected(0x5c01, new half3(256.125f));
CheckAllComponentsForExpected(0x5c02, new half3(256.375f));
CheckAllComponentsForExpected(0xbc01, new half3(-1.00048828125f));
CheckAllComponentsForExpected(0xdc01, new half3(-256.125f));
CheckAllComponentsForExpected(0xdc02, new half3(-256.375f));

CheckAllComponentsForExpected(0x3c01, new half3(1.00048828125));
CheckAllComponentsForExpected(0x5c01, new half3(256.125));
CheckAllComponentsForExpected(0x5c02, new half3(256.375));
CheckAllComponentsForExpected(0xbc01, new half3(-1.00048828125));
CheckAllComponentsForExpected(0xdc01, new half3(-256.125));
CheckAllComponentsForExpected(0xdc02, new half3(-256.375));
}

[TestCompiler]
public static void half4_check_rounding()
{
// 1 ulp away from a representable half.
CheckAllComponentsForExpected(0x3c01, new half4(1.00097668170928955078f));
CheckAllComponentsForExpected(0x3c01, new half4(1.00097644329071044922f));

CheckAllComponentsForExpected(0x3c01, new half4(1.00097668170928955078));
CheckAllComponentsForExpected(0x3c01, new half4(1.00097644329071044922));

// 1 ulp away from the midpoint between two representable halfs.
CheckAllComponentsForExpected(0x5c00, new half4(256.124969482421875f));
CheckAllComponentsForExpected(0x5c01, new half4(256.125030517578125f));
CheckAllComponentsForExpected(0x5c01, new half4(256.374969482421875f));
CheckAllComponentsForExpected(0x5c02, new half4(256.375030517578125f));
CheckAllComponentsForExpected(0xdc00, new half4(-256.124969482421875f));
CheckAllComponentsForExpected(0xdc01, new half4(-256.125030517578125f));
CheckAllComponentsForExpected(0xdc01, new half4(-256.374969482421875f));
CheckAllComponentsForExpected(0xdc02, new half4(-256.375030517578125f));

CheckAllComponentsForExpected(0x5c00, new half4(256.124969482421875));
CheckAllComponentsForExpected(0x5c01, new half4(256.125030517578125));
CheckAllComponentsForExpected(0x5c01, new half4(256.374969482421875));
CheckAllComponentsForExpected(0x5c02, new half4(256.375030517578125));
CheckAllComponentsForExpected(0xdc00, new half4(-256.124969482421875));
CheckAllComponentsForExpected(0xdc01, new half4(-256.125030517578125));
CheckAllComponentsForExpected(0xdc01, new half4(-256.374969482421875));
CheckAllComponentsForExpected(0xdc02, new half4(-256.375030517578125));

// At the midpoint between two representable halfs.
CheckAllComponentsForExpected(0x3c01, new half4(1.00048828125f));
CheckAllComponentsForExpected(0x5c01, new half4(256.125f));
CheckAllComponentsForExpected(0x5c02, new half4(256.375f));
CheckAllComponentsForExpected(0xbc01, new half4(-1.00048828125f));
CheckAllComponentsForExpected(0xdc01, new half4(-256.125f));
CheckAllComponentsForExpected(0xdc02, new half4(-256.375f));

CheckAllComponentsForExpected(0x3c01, new half4(1.00048828125));
CheckAllComponentsForExpected(0x5c01, new half4(256.125));
CheckAllComponentsForExpected(0x5c02, new half4(256.375));
CheckAllComponentsForExpected(0xbc01, new half4(-1.00048828125));
CheckAllComponentsForExpected(0xdc01, new half4(-256.125));
CheckAllComponentsForExpected(0xdc02, new half4(-256.375));
}

[TestCase]
public static void half_EqualsObjectOverride()
{
Expand Down
16 changes: 16 additions & 0 deletions src/Unity.Mathematics.CodeGen~/VectorGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,16 @@ private void GenerateDebuggerTypeProxy(StringBuilder str)
str.Append("\t\t}\n\n");
}

void AppendIfHalf(StringBuilder str, string toAppend)
{
if (m_BaseType == "half")
{
str.AppendFormat(toAppend);
}
}

static readonly string halfRoundingModeRemark = "\t\t/// <remarks>The rounding mode for this conversion is round away from zero (toward infinity).</remarks>\n";

private void GenerateConversion(StringBuilder str, StringBuilder opStr, StringBuilder mathStr, string sourceBaseType, bool isExplicit, bool isScalar)
{
string sourceType = isScalar ? sourceBaseType : ToTypeName(sourceBaseType, m_Rows, m_Columns);
Expand All @@ -372,12 +382,15 @@ private void GenerateConversion(StringBuilder str, StringBuilder opStr, StringBu
{
str.AppendFormat("\t\t/// <summary>Constructs a {0} {1} from a single {2} value by converting it to {3} and assigning it to every component.</summary>\n", m_TypeName, dstTypeCategory, sourceType, m_BaseType);
str.AppendFormat($"\t\t/// <param name=\"v\">{sourceType} to convert to {m_TypeName}</param>\n");
AppendIfHalf(str, halfRoundingModeRemark);

mathStr.AppendFormat("\t\t/// <summary>Returns a {0} {1} constructed from a single {2} value by converting it to {3} and assigning it to every component.</summary>\n", m_TypeName, dstTypeCategory, sourceType, m_BaseType);
mathStr.AppendFormat($"\t\t/// <param name=\"v\">{sourceType} to convert to {m_TypeName}</param>\n");
AppendIfHalf(mathStr, halfRoundingModeRemark);

opStr.AppendFormat("\t\t/// <summary>{0} converts a single {1} value to a {2} {3} by converting it to {4} and assigning it to every component.</summary>\n", plicitlyString, sourceType, m_TypeName, dstTypeCategory, m_BaseType);
opStr.AppendFormat($"\t\t/// <param name=\"v\">{sourceType} to convert to {m_TypeName}</param>\n");
AppendIfHalf(opStr, halfRoundingModeRemark);
}
else
{
Expand All @@ -397,12 +410,15 @@ private void GenerateConversion(StringBuilder str, StringBuilder opStr, StringBu
{
str.AppendFormat("\t\t/// <summary>Constructs a {0} {1} from a {2} {1} by componentwise conversion.</summary>\n", m_TypeName, dstTypeCategory, sourceType);
str.AppendFormat($"\t\t/// <param name=\"v\">{sourceType} to convert to {m_TypeName}</param>\n");
AppendIfHalf(str, halfRoundingModeRemark);

mathStr.AppendFormat("\t\t/// <summary>Return a {0} {1} constructed from a {2} {1} by componentwise conversion.</summary>\n", m_TypeName, dstTypeCategory, sourceType);
mathStr.AppendFormat($"\t\t/// <param name=\"v\">{sourceType} to convert to {m_TypeName}</param>\n");
AppendIfHalf(mathStr, halfRoundingModeRemark);

opStr.AppendFormat("\t\t/// <summary>{0} converts a {1} {2} to a {3} {2} by componentwise conversion.</summary>\n", plicitlyString, sourceType, dstTypeCategory, m_TypeName);
opStr.AppendFormat($"\t\t/// <param name=\"v\">{sourceType} to convert to {m_TypeName}</param>\n");
AppendIfHalf(opStr, halfRoundingModeRemark);
}
}

Expand Down
6 changes: 6 additions & 0 deletions src/Unity.Mathematics/half.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public half(half x)
}

/// <summary>Constructs a half value from a float value.</summary>
/// <remarks>The rounding mode for this conversion is round away from zero (toward infinity).</remarks>
/// <param name="v">The single precision float value to convert to half.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public half(float v)
Expand All @@ -56,6 +57,7 @@ public half(float v)
}

/// <summary>Constructs a half value from a double value.</summary>
/// <remarks>The rounding mode for this conversion is round away from zero (toward infinity).</remarks>
/// <param name="v">The double precision float value to convert to half.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public half(double v)
Expand All @@ -64,12 +66,14 @@ public half(double v)
}

/// <summary>Explicitly converts a float value to a half value.</summary>
/// <remarks>The rounding mode for this conversion is round away from zero (toward infinity).</remarks>
/// <param name="v">The single precision float value to convert to half.</param>
/// <returns>The converted half value.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator half(float v) { return new half(v); }

/// <summary>Explicitly converts a double value to a half value.</summary>
/// <remarks>The rounding mode for this conversion is round away from zero (toward infinity).</remarks>
/// <param name="v">The double precision float value to convert to half.</param>
/// <returns>The converted half value.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand Down Expand Up @@ -147,12 +151,14 @@ public static partial class math
public static half half(half x) { return new half(x); }

/// <summary>Returns a half value constructed from a float value.</summary>
/// <remarks>The rounding mode for this conversion is round away from zero (toward infinity).</remarks>
/// <param name="v">The single precision float value to convert to half.</param>
/// <returns>The constructed half value.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static half half(float v) { return new half(v); }

/// <summary>Returns a half value constructed from a double value.</summary>
/// <remarks>The rounding mode for this conversion is round away from zero (toward infinity).</remarks>
/// <param name="v">The double precision float value to convert to half.</param>
/// <returns>The constructed half value.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand Down
Loading