From c4ebc1b5f6eb06763751150ad788cc5b1d890c4b Mon Sep 17 00:00:00 2001 From: Shahab Layehgi Date: Wed, 25 Mar 2020 15:19:09 -0700 Subject: [PATCH 1/3] [SYCL] Fix float to half-type conversion Fix float to half conversion for a range of small numbers that could be represnted normally in float, but are subnormal in half-type. Signed-off-by: Shahab Layehgi --- sycl/source/half_type.cpp | 46 +++++++++++------------------ sycl/test/basic_tests/half_type.cpp | 4 +++ 2 files changed, 22 insertions(+), 28 deletions(-) diff --git a/sycl/source/half_type.cpp b/sycl/source/half_type.cpp index b7b06f767cd34..47645dc7b569e 100644 --- a/sycl/source/half_type.cpp +++ b/sycl/source/half_type.cpp @@ -25,42 +25,32 @@ static uint16_t float2Half(const float &Val) { const uint32_t Frac32 = Bits & 0x7fffff; // Extract the exponent from the float value const uint8_t Exp32 = (Bits & 0x7f800000) >> 23; - const int8_t Exp32Diff = Exp32 - 127; + const int16_t Exp32Diff = Exp32 - 127; - uint16_t Exp16 = 0; + // intialize to 0, covers the case for 0 and small numbers + uint16_t Exp16=0, Frac16=0; - // convert 23-bit mantissa to 10-bit mantissa. - uint16_t Frac16 = Frac32 >> 13; - // Round the mantissa as given in OpenCL spec section : 6.1.1.1 The half data - // type. - if (Frac32 >> 12 & 0x01) - Frac16 += 1; - - if (__builtin_expect(Exp32 == 0xff || Exp32Diff > 15, 0)) { + if (__builtin_expect(Exp32Diff > 15, 0)) { + // Infinity and big numbers convert to infinity Exp16 = 0x1f; - } else if (__builtin_expect(Exp32 == 0 || Exp32Diff < -14, 0)) { - Exp16 = 0; + } else if (__builtin_expect(Exp32Diff < -14, 0)) { + // subnormals + Frac16 = (Frac32 | (1<<23)) >> (-Exp32Diff-1); } else { + // normal range for half type Exp16 = Exp32Diff + 15; + // convert 23-bit mantissa to 10-bit mantissa. + Frac16 = Frac32 >> 13; + // Round the mantissa as given in OpenCL spec section : 6.1.1.1 The half data + // type. + if (Frac32 >> 12 & 0x01) + Frac16 += 1; } - if (__builtin_expect(Exp32 == 0xff && Frac32 != 0 && Frac16 == 0, 0)) { - // corner case 1: NaN - // This case happens when FP32 value is NaN whose the fraction part - // transformed to FP16 counterpart is truncated to 0. We need to flip the - // high bit to 1 to make it distinguished from inf. + if (__builtin_expect(Exp32 == 0xff && Frac32 != 0, 0)) { + // corner case: FP32 is NaN + Exp16 = 0x1F; Frac16 = 0x200; - } else if (__builtin_expect(Exp32 == 0 || (Exp16 == 0x1f && Exp32 != 0xff), - 0)) { - // corner case 2: subnormal - // All FP32 subnormal values are under the range of FP16 so the fraction - // part is set to 0. - // corner case 3: overflow - Frac16 = 0; - } else if (__builtin_expect(Exp16 == 0 && Exp32 != 0, 0)) { - // corner case 4: underflow - // We use `truncate` mode here. - Frac16 = 0x100 | (Frac16 >> 2); } // Compose the final FP16 binary diff --git a/sycl/test/basic_tests/half_type.cpp b/sycl/test/basic_tests/half_type.cpp index ea5d744004fb6..5958fced13d6c 100644 --- a/sycl/test/basic_tests/half_type.cpp +++ b/sycl/test/basic_tests/half_type.cpp @@ -245,6 +245,10 @@ int main() { // subnormal assert(bitwise_comparison_fp16(9.8E-45, 0)); assert(bitwise_comparison_fp16(-9.8E-45, 32768)); + uint32_t subnormal_in_16 = 0x38200000; + // verify 0.000038146972 > 0.0000382 + assert( + bitwise_comparison_fp16(reinterpret_cast(subnormal_in_16), 0x0280)); // overflow assert(bitwise_comparison_fp16(half(55504) * 3, 31744)); assert(bitwise_comparison_fp16(half(-55504) * 3, 64512)); From d13241364855eddd4bf860f5dfa5035f532a5e62 Mon Sep 17 00:00:00 2001 From: Shahab Layehgi Date: Thu, 26 Mar 2020 00:25:09 -0700 Subject: [PATCH 2/3] [SYCL] Use clang-format to fix formatting Signed-off-by: Shahab Layehgi --- sycl/source/half_type.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sycl/source/half_type.cpp b/sycl/source/half_type.cpp index 47645dc7b569e..2ff488c4747d5 100644 --- a/sycl/source/half_type.cpp +++ b/sycl/source/half_type.cpp @@ -28,21 +28,21 @@ static uint16_t float2Half(const float &Val) { const int16_t Exp32Diff = Exp32 - 127; // intialize to 0, covers the case for 0 and small numbers - uint16_t Exp16=0, Frac16=0; + uint16_t Exp16 = 0, Frac16 = 0; if (__builtin_expect(Exp32Diff > 15, 0)) { // Infinity and big numbers convert to infinity Exp16 = 0x1f; } else if (__builtin_expect(Exp32Diff < -14, 0)) { // subnormals - Frac16 = (Frac32 | (1<<23)) >> (-Exp32Diff-1); + Frac16 = (Frac32 | (1 << 23)) >> (-Exp32Diff - 1); } else { // normal range for half type Exp16 = Exp32Diff + 15; // convert 23-bit mantissa to 10-bit mantissa. Frac16 = Frac32 >> 13; - // Round the mantissa as given in OpenCL spec section : 6.1.1.1 The half data - // type. + // Round the mantissa as given in OpenCL spec section : 6.1.1.1 The half + // data type. if (Frac32 >> 12 & 0x01) Frac16 += 1; } @@ -57,7 +57,7 @@ static uint16_t float2Half(const float &Val) { uint16_t Ret = 0; Ret |= Sign; Ret |= Exp16 << 10; - Ret += Frac16;// Add the carry bit from operation Frac16 += 1; + Ret += Frac16; // Add the carry bit from operation Frac16 += 1; return Ret; } @@ -171,7 +171,7 @@ bool operator==(const half &LHS, const half &RHS) { } bool operator!=(const half &LHS, const half &RHS) { return !(LHS == RHS); } -} // namespace half_impl +} // namespace host_half_impl } // namespace detail } // namespace sycl From 9449ccd9a101191d7e7e3fa41dd3d8a4368b2436 Mon Sep 17 00:00:00 2001 From: Shahab Layehgi Date: Thu, 2 Apr 2020 22:03:15 -0700 Subject: [PATCH 3/3] [SYCL] Address review comments Check for case Exp32Diff<-24 to prevent shift>32 bits Update comment in test file Signed-off-by: Shahab Layehgi --- sycl/source/half_type.cpp | 8 ++++---- sycl/test/basic_tests/half_type.cpp | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/sycl/source/half_type.cpp b/sycl/source/half_type.cpp index 2ff488c4747d5..093dcaf71e772 100644 --- a/sycl/source/half_type.cpp +++ b/sycl/source/half_type.cpp @@ -33,10 +33,7 @@ static uint16_t float2Half(const float &Val) { if (__builtin_expect(Exp32Diff > 15, 0)) { // Infinity and big numbers convert to infinity Exp16 = 0x1f; - } else if (__builtin_expect(Exp32Diff < -14, 0)) { - // subnormals - Frac16 = (Frac32 | (1 << 23)) >> (-Exp32Diff - 1); - } else { + } else if (__builtin_expect(Exp32Diff > -14, 0)) { // normal range for half type Exp16 = Exp32Diff + 15; // convert 23-bit mantissa to 10-bit mantissa. @@ -45,6 +42,9 @@ static uint16_t float2Half(const float &Val) { // data type. if (Frac32 >> 12 & 0x01) Frac16 += 1; + } else if (__builtin_expect(Exp32Diff > -24, 0)) { + // subnormals + Frac16 = (Frac32 | (uint32_t(1) << 23)) >> (-Exp32Diff - 1); } if (__builtin_expect(Exp32 == 0xff && Frac32 != 0, 0)) { diff --git a/sycl/test/basic_tests/half_type.cpp b/sycl/test/basic_tests/half_type.cpp index 5958fced13d6c..dfff5037cdbf1 100644 --- a/sycl/test/basic_tests/half_type.cpp +++ b/sycl/test/basic_tests/half_type.cpp @@ -246,7 +246,7 @@ int main() { assert(bitwise_comparison_fp16(9.8E-45, 0)); assert(bitwise_comparison_fp16(-9.8E-45, 32768)); uint32_t subnormal_in_16 = 0x38200000; - // verify 0.000038146972 > 0.0000382 + // verify 0.000038146972 converts to 0.0000382 assert( bitwise_comparison_fp16(reinterpret_cast(subnormal_in_16), 0x0280)); // overflow