-
Notifications
You must be signed in to change notification settings - Fork 251
Expand file tree
/
Copy pathfp16_emu.cpp
More file actions
118 lines (99 loc) · 3.35 KB
/
Copy pathfp16_emu.cpp
File metadata and controls
118 lines (99 loc) · 3.35 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
/*
* SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "./utils/fp16_emu.h"
#define STATIC_ASSERT(cond) \
{ \
static_assert(cond, "static_assert failed."); \
}
// Host functions for converting between FP32 and FP16 formats
// Paulius Micikevicius (pauliusm@nvidia.com)
half1
cpu_float2half_rn(float f) {
void* f_ptr = &f;
unsigned x = *((int*)f_ptr);
unsigned u = (x & 0x7fffffff), remainder, shift, lsb, lsb_s1, lsb_m1;
unsigned sign, exponent, mantissa;
__half_raw hr;
// Get rid of +NaN/-NaN case first.
if (u > 0x7f800000) {
hr.x = 0x7fffU;
// Add an indirection to get around type aliasing check
void* hr_ptr = &hr;
return *reinterpret_cast<half1*>(hr_ptr);
}
sign = ((x >> 16) & 0x8000);
// Get rid of +Inf/-Inf, +0/-0.
if (u > 0x477fefff) {
hr.x = static_cast<unsigned short>(sign | 0x7c00U);
// Add an indirection to get around type aliasing check
void* hr_ptr = &hr;
return *reinterpret_cast<half1*>(hr_ptr);
}
if (u < 0x33000001) {
hr.x = static_cast<unsigned short>(sign | 0x0000U);
// Add an indirection to get around type aliasing check
void* hr_ptr = &hr;
return *reinterpret_cast<half1*>(hr_ptr);
}
exponent = ((u >> 23) & 0xff);
mantissa = (u & 0x7fffff);
if (exponent > 0x70) {
shift = 13;
exponent -= 0x70;
} else {
shift = 0x7e - exponent;
exponent = 0;
mantissa |= 0x800000;
}
lsb = (1 << shift);
lsb_s1 = (lsb >> 1);
lsb_m1 = (lsb - 1);
// Round to nearest even.
remainder = (mantissa & lsb_m1);
mantissa >>= shift;
if (remainder > lsb_s1 || (remainder == lsb_s1 && (mantissa & 0x1))) {
++mantissa;
if (!(mantissa & 0x3ff)) {
++exponent;
mantissa = 0;
}
}
hr.x = static_cast<unsigned short>((sign | (exponent << 10) | mantissa));
// Add an indirection to get around type aliasing check
void* hr_ptr = &hr;
return *reinterpret_cast<half1*>(hr_ptr);
}
float
cpu_half2float(half1 h) {
STATIC_ASSERT(sizeof(int) == sizeof(float));
// Add an indirection to get around type aliasing check
void* h_ptr = &h;
__half_raw hr = *reinterpret_cast<__half_raw*>(h_ptr);
unsigned sign = ((hr.x >> 15) & 1);
unsigned exponent = ((hr.x >> 10) & 0x1f);
unsigned mantissa = ((hr.x & 0x3ff) << 13);
if (exponent == 0x1f) { /* NaN or Inf */
mantissa = (mantissa ? (sign = 0, 0x7fffff) : 0);
exponent = 0xff;
} else if (!exponent) { /* Denorm or Zero */
if (mantissa) {
unsigned int msb;
exponent = 0x71;
do {
msb = (mantissa & 0x400000);
mantissa <<= 1; /* normalize */
--exponent;
} while (!msb);
mantissa &= 0x7fffff; /* 1.mantissa is implicit */
}
} else {
exponent += 0x70;
}
int temp = ((sign << 31) | (exponent << 23) | mantissa);
// Add an indirection to get around type aliasing check
void* temp_ptr = &temp;
float* res_ptr = reinterpret_cast<float*>(temp_ptr);
return *res_ptr;
}