Flutter Engine
The Flutter Engine
Float16Test.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2016 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "src/base/SkHalf.h"
9#include "src/base/SkRandom.h"
10#include "src/base/SkVx.h"
11#include "tests/Test.h"
12
13#include <cmath>
14#include <cstdint>
15#include <cstring>
16
17// float = s[31] e[30:23] m[22:0]
18static constexpr uint32_t kF32_Sign = 1 << 31;
19static constexpr uint32_t kF32_Exp = 255 << 23;
20static constexpr uint32_t kF32_Mant = ~(kF32_Sign | kF32_Exp);
21static constexpr int kF32_Bias = 127;
22
23// half = s[15] e[14:10] m[9:0]
24static constexpr uint32_t kF16_Sign = 1 << 15;
25static constexpr uint32_t kF16_Exp = 31 << 10;
26static constexpr uint32_t kF16_Mant = ~(kF16_Sign | kF16_Exp);
27static constexpr int kF16_Bias = 15;
28
29DEF_TEST(FloatToHalf, r) {
30#if 0
31 // Exhaustive test (slow)
32 for (uint64_t bits = 0; bits <= 0xffffffff; bits++) {
33 if (bits % (1 << 24) == 0) {
34 SkDebugf("progress 0x%08X\n", (int) bits);
35 }
36#else
37 // Check all 8-bit exponents and all 10-bit upper mantissas, with a combination of all 0s,
38 // all 1s, and random bits in the remaining 13 fractional mantissa bits.
39 static constexpr int kTestCount = /*sign*/2 * /*exp*/255 * /*man*/1024 * /*frac*/8;
40 SkRandom rand;
41 for (int i = 0; i < kTestCount; ++i) {
42 uint32_t sign = (i & 1) << 31;
43 uint32_t exp = ((i >> 1) & 255) << 23;
44 uint32_t man = ((i >> 9) & 1023) << 13;
45 uint32_t frac = ((i >> 19) & 7); // 0 and 1 are special, 6 other values are random bits
46 uint64_t bits = sign | exp | man | ((frac == 0) ? 0 : // all 0s in lost fraction
47 (frac == 1) ? (1 << 13) - 1 // all 1s in lost fraction
48 : rand.nextBits(13)); // random lost bits
49#endif
50
51 float f = SkBits2Float(bits);
52 if (SkIsNaN(f)) {
53#ifndef SK_DEBUG
54 // We want float->half and half->float to play well with infinities and max
55 // representable values in the 16-bit precision, but NaNs should have been caught ahead
56 // of time, so the conversion logic is allowed to convert them to infinities in release
57 // builds. We skip calling `to_half` in debug since it asserts that NaN isn't passed in.
58 uint16_t actual2 = to_half(skvx::float2{f})[0];
59 uint16_t actual4 = to_half(skvx::float4{f})[0];
60 REPORTER_ASSERT(r, (actual2 & kF16_Exp) == kF16_Exp);
61 REPORTER_ASSERT(r, (actual4 & kF16_Exp) == kF16_Exp);
62#endif
63 continue;
64 }
65
66 uint32_t s32 = (uint32_t) bits & kF32_Sign;
67 uint32_t e32 = (uint32_t) bits & kF32_Exp;
68 uint32_t m32 = (uint32_t) bits & kF32_Mant;
69
70 // Half floats can represent a real exponent from -14 to 15. Anything less than that would
71 // need to be a denorm, which is flushed to zero, or overflows and becomes infinity.
72 int e = (int) (e32 >> 23) - kF32_Bias; // the true signed exponent
73
74 uint32_t s16 = s32 >> 16;
75 uint32_t e16;
76 uint32_t m16;
77 if (e < -kF16_Bias-10 || (e == -kF16_Bias-10 && m32 <= 0)) {
78 // Rounds to zero
79 e16 = 0;
80 m16 = 0;
81 } else if ((e32 | m32) < 0x38fe'0000) {
82 // A subnormal non-zero f16 value
83 e16 = 0;
84 m16 = 0xffff & sk_bit_cast<uint32_t>(0.5f + SkBits2Float(e32 | m32));
85 } else if ((e32 | m32) < 0x3880'0000) {
86 // Rounds up to smallest normal f16 (2^-14)
87 e16 = 1;
88 m16 = 0;
89 } else if (e > kF16_Bias) {
90 // Either f32 infinity or a value larger than what rounds down to the max normal half.
91 e16 = kF16_Exp;
92 m16 = 0;
93 } else {
94 // A normal half value, which is rounded towards nearest even.
95 e16 = (uint32_t) (e + kF16_Bias) << 10;
96 SkASSERT((e16 & ~kF16_Exp) == 0);
97
98 // round to nearest even
99 m32 += 0xfff + ((m32>>13)&1);
100
101 if (m32 > kF32_Mant) {
102 // overflow
103 e16 += (1 << 10);
104 m16 = 0;
105 } else {
106 m16 = m32 >> 13;
107 }
108 }
109
110 // Expected conversion from f32 to f16
111 uint16_t expected = s16 | e16 | m16;
112 uint16_t actual2 = to_half(skvx::float2{f})[0];
113 uint16_t actual4 = to_half(skvx::float4{f})[0];
114 REPORTER_ASSERT(r, expected == actual2);
115 REPORTER_ASSERT(r, expected == actual4);
116 }
117}
118
119DEF_TEST(FloatToHalf_Constants, r) {
120 auto to_half = [](float f) { return skvx::to_half(skvx::float4{f})[0]; };
121 REPORTER_ASSERT(r, 0 == to_half(0.f));
122 REPORTER_ASSERT(r, kF16_Sign == to_half(-0.f));
123 REPORTER_ASSERT(r, SK_Half1 == to_half(1.f));
124 REPORTER_ASSERT(r, (kF16_Sign | SK_Half1) == to_half(-1.f));
125 REPORTER_ASSERT(r, SK_HalfMax == to_half(65504.f));
126 REPORTER_ASSERT(r, SK_HalfMin == to_half(1.f / (1 << 14)));
127}
128
130 for (uint32_t bits = 0; bits <= 0xffff; bits++) {
131 uint32_t s16 = bits & kF16_Sign;
132 uint32_t e16 = bits & kF16_Exp;
133 uint32_t m16 = bits & kF16_Mant;
134
135 float actual2 = from_half(skvx::half2{(uint16_t) bits})[0];
136 float actual4 = from_half(skvx::half4{(uint16_t) bits})[0];
137
138 if (e16 == 0) {
139 // De-normal f16 or a zero = 2^-14 * 0.[m16] = 2^-14 * 2^-10 * [m16].0
140 float expected = (1.f / (1 << 14)) * (1.f / (1 << 10)) * m16;
141 if (s16 != 0) {
142 expected *= -1.f;
143 }
144 REPORTER_ASSERT(r, actual2 == expected);
145 REPORTER_ASSERT(r, actual4 == expected);
146 } else if (e16 == kF16_Exp) {
147 if (m16 != 0) {
148 // A NaN stays NaN
149 REPORTER_ASSERT(r, SkIsNaN(actual2));
150 REPORTER_ASSERT(r, SkIsNaN(actual4));
151 } else {
152 // +/- infinity stays infinite
153 if (s16) {
156 } else {
157 REPORTER_ASSERT(r, actual2 == SK_ScalarInfinity);
158 REPORTER_ASSERT(r, actual4 == SK_ScalarInfinity);
159 }
160 }
161 } else {
162 // A normal f16 is exactly representable in f32
163 uint32_t s32 = s16 << 16;
164 uint32_t e32 = ((e16 >> 10) + kF32_Bias - kF16_Bias) << 23;
165 uint32_t m32 = m16 << 13;
166
167 float expected = SkBits2Float(s32 | e32 | m32);
168 REPORTER_ASSERT(r, actual2 == expected);
169 REPORTER_ASSERT(r, actual4 == expected);
170 }
171 }
172}
static constexpr uint32_t kF16_Exp
Definition: Float16Test.cpp:25
DEF_TEST(FloatToHalf, r)
Definition: Float16Test.cpp:29
static constexpr uint32_t kF32_Sign
Definition: Float16Test.cpp:18
static constexpr uint32_t kF32_Mant
Definition: Float16Test.cpp:20
static constexpr uint32_t kF16_Sign
Definition: Float16Test.cpp:24
static constexpr uint32_t kF32_Exp
Definition: Float16Test.cpp:19
static constexpr int kF32_Bias
Definition: Float16Test.cpp:21
static constexpr int kF16_Bias
Definition: Float16Test.cpp:27
static constexpr uint32_t kF16_Mant
Definition: Float16Test.cpp:26
#define SkASSERT(cond)
Definition: SkAssert.h:116
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
static float SkBits2Float(uint32_t bits)
Definition: SkFloatBits.h:48
static constexpr bool SkIsNaN(T x)
static constexpr uint16_t SK_HalfMax
Definition: SkHalf.h:21
static constexpr uint16_t SK_Half1
Definition: SkHalf.h:23
static constexpr uint16_t SK_HalfMin
Definition: SkHalf.h:20
static int sign(SkScalar x)
Definition: SkPath.cpp:2205
#define SK_ScalarInfinity
Definition: SkScalar.h:26
#define SK_ScalarNegativeInfinity
Definition: SkScalar.h:27
#define REPORTER_ASSERT(r, cond,...)
Definition: Test.h:286
uint32_t nextBits(unsigned bitCount)
Definition: SkRandom.h:72
float HalfToFloat(uint16_t half)
SIN Vec< N, float > from_half(const Vec< N, uint16_t > &x)
Definition: SkVx.h:790
SIN Vec< N, uint16_t > to_half(const Vec< N, float > &x)
Definition: SkVx.h:750
Definition: SkVx.h:83