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 0 : uint16_t hfSign; 84 0 : uint16_t hfMan; 85 0 : 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 6 : uint16_t mRet; 121 6 : 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