Line data Source code
1 : /**
2 : * Copyright (c) 2025 Huawei Technologies Co., Ltd.
3 : * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4 : * CANN Open Software License Agreement Version 2.0 (the "License").
5 : * Please refer to the License for details. You may not use this file except in compliance with the License.
6 : * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7 : * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8 : * See LICENSE in the root of the software repository for the full text of the License.
9 : */
10 :
11 : #include "fp16_impl.h"
12 :
13 : namespace acl {
14 : /**
15 : * @ingroup fp16_t global filed
16 : * @brief round mode of last valid digital
17 : */
18 :
19 : union TypeUnion {
20 : float32_t fVal;
21 : uint32_t uVal;
22 : };
23 :
24 0 : static void ExtractFP16(const uint16_t val, uint16_t* const s, int16_t* const e, uint16_t* const m)
25 : {
26 : // 1.Extract
27 0 : *s = FP16_EXTRAC_SIGN(val);
28 0 : *e = static_cast<int16_t>(FP16_EXTRAC_EXP(val));
29 0 : *m = FP16_EXTRAC_MAN(val);
30 :
31 : // Denormal
32 0 : if ((*e) == 0) {
33 0 : *e = 1;
34 : }
35 0 : }
36 :
37 : /**
38 : * @ingroup fp16_t static method
39 : * @param [in] man truncated mantissa
40 : * @param [in] shiftOut left shift bits based on ten bits
41 : * @brief judge whether to add one to the result while converting fp16_t to other datatype
42 : * @return Return true if add one, otherwise false
43 : */
44 3 : static bool IsRoundOne(const uint64_t man, const uint16_t truncLen)
45 : {
46 3 : uint64_t mask0 = 0x4UL;
47 3 : uint64_t mask1 = 0x2UL;
48 : uint64_t mask2;
49 3 : const uint64_t shiftOut = static_cast<uint64_t>(truncLen - 2U); // shift 2 byte
50 3 : mask0 = mask0 << shiftOut;
51 3 : mask1 = mask1 << shiftOut;
52 3 : mask2 = mask1 - 1U;
53 :
54 3 : const bool lastBit = ((man & mask0) > 0UL);
55 3 : const bool truncHigh = ((man & mask1) > 0UL);
56 3 : const bool truncLeft = ((man & mask2) > 0UL);
57 3 : return (truncHigh && (truncLeft || lastBit));
58 : }
59 :
60 : /**
61 : * @ingroup fp16_t public method
62 : * @param [in] exp exponent of fp16_t value
63 : * @param [in] man exponent of fp16_t value
64 : * @brief normalize fp16_t value
65 : * @return
66 : */
67 6 : static void Fp16Normalize(uint16_t& expVal, uint16_t& man)
68 : {
69 6 : if (expVal >= FP16_MAX_EXP) {
70 0 : expVal = FP16_MAX_EXP - 1U;
71 0 : man = FP16_MAX_MAN;
72 0 : return;
73 : }
74 6 : if ((expVal == 0U) && (man == FP16_MAN_HIDE_BIT)) {
75 0 : expVal++;
76 0 : man = 0U;
77 0 : return;
78 : }
79 : }
80 :
81 0 : float32_t Fp16ToFloat(const uint16_t val)
82 : {
83 : uint16_t hfSign;
84 : uint16_t hfMan;
85 : int16_t hfExp;
86 0 : ExtractFP16(val, &hfSign, &hfExp, &hfMan);
87 :
88 0 : while ((hfMan != 0U) && ((hfMan & FP16_MAN_HIDE_BIT) == 0U)) {
89 0 : hfMan <<= 1U;
90 0 : hfExp--;
91 : }
92 :
93 : uint32_t eRet;
94 : uint32_t mRet;
95 0 : if (hfMan == 0U) {
96 0 : eRet = 0U;
97 0 : mRet = 0U;
98 : } else {
99 0 : eRet = static_cast<uint32_t>(hfExp + static_cast<int16_t>(FP32_EXP_BIAS - FP16_EXP_BIAS));
100 0 : mRet = static_cast<uint32_t>(hfMan & FP16_MAN_MASK);
101 0 : mRet = mRet << (FP32_MAN_LEN - FP16_MAN_LEN);
102 : }
103 :
104 0 : const uint32_t sRet = hfSign;
105 : TypeUnion u;
106 0 : u.uVal = FP32_CONSTRUCTOR(sRet, eRet, mRet);
107 0 : const auto ret = u.fVal;
108 0 : return ret;
109 : }
110 :
111 6 : uint16_t FloatToFp16(const float32_t val)
112 : {
113 : TypeUnion u;
114 6 : u.fVal = val;
115 6 : const uint32_t ui32V = u.uVal; // 1:8:23bit sign:exp:man
116 6 : const auto sRet = static_cast<uint16_t>((ui32V & FP32_SIGN_MASK) >> FP32_SIGN_INDEX); // 4Byte->2Byte
117 6 : const uint32_t eF = (ui32V & FP32_EXP_MASK) >> FP32_MAN_LEN; // 8 bit exponent
118 6 : uint32_t mF = (ui32V & FP32_MAN_MASK); // 23 bit mantissa dont't need to care about denormal
119 :
120 : uint16_t mRet;
121 : uint16_t eRet;
122 : // Exponent overflow/NaN converts to signed inf/NaN
123 6 : if (eF > 0x8FU) { // 0x8Fu:142=127+15
124 2 : eRet = FP16_MAX_EXP - 1U;
125 2 : mRet = FP16_MAX_MAN;
126 4 : } else if (eF <= 0x70U) { // 0x70u:112=127-15 Exponent underflow converts to denormalized half or signed zero
127 1 : eRet = 0U;
128 1 : if (eF >= 0x67U) { // 0x67:103=127-24 Denormal
129 0 : mF = (mF | FP32_MAN_HIDE_BIT);
130 0 : const uint16_t shiftOut = FP32_MAN_LEN;
131 0 : const uint64_t mTmp = (static_cast<uint64_t>(mF)) << (eF - 0x67U);
132 :
133 0 : const bool needRound = IsRoundOne(mTmp, shiftOut);
134 0 : mRet = static_cast<uint16_t>(mTmp >> shiftOut);
135 0 : if (needRound) {
136 0 : mRet++;
137 : }
138 1 : } else if ((eF == 0x66U) && (mF > 0U)) { // 0x66:102 Denormal 0<f_v<min(Denormal)
139 0 : mRet = 1U;
140 : } else {
141 1 : mRet = 0U;
142 : }
143 : } else { // Regular case with no overflow or underflow
144 3 : const uint32_t mLenDelta = FP32_MAN_LEN - FP16_MAN_LEN;
145 3 : eRet = static_cast<uint16_t>(eF - 0x70U);
146 3 : const bool needRound = IsRoundOne(static_cast<uint64_t>(mF), static_cast<uint16_t>(mLenDelta));
147 3 : mRet = static_cast<uint16_t>(mF >> mLenDelta);
148 3 : if (needRound) {
149 0 : mRet++;
150 : }
151 3 : if ((mRet & FP16_MAN_HIDE_BIT) != 0U) {
152 0 : eRet++;
153 : }
154 : }
155 :
156 6 : Fp16Normalize(eRet, mRet);
157 6 : const uint16_t ret = static_cast<uint16_t>(FP16_CONSTRUCTOR(sRet, eRet, mRet));
158 6 : return ret;
159 : }
160 : } // namespace acl
|