softfloat之f32_to_bf16
bfloat16_t f32_to_bf16( float32_t a )
{
union ui32_f32 uA;
uint_fast32_t uiA;
bool sign;
int_fast16_t exp;
uint_fast32_t frac;
struct commonNaN commonNaN;
uint_fast16_t uiZ, frac16;
union ui16_bf16 uZ;
/*------------------------------------------------------------------------
*------------------------------------------------------------------------*/
uA.f = a;
uiA = uA.ui;
sign = signF32UI( uiA );
exp = expF32UI( uiA );
frac = fracF32UI( uiA );
/*------------------------------------------------------------------------
*------------------------------------------------------------------------*/
// infinity or NaN cases
if ( exp == 0xFF ) {
if ( frac ) {
// NaN case
softfloat_f32UIToCommonNaN( uiA, &commonNaN );
uiZ = softfloat_commonNaNToBF16UI( &commonNaN );
} else {
// infinity case
uiZ = packToBF16UI( sign, 0xFF, 0 );
}
goto uiZ;
}
/*------------------------------------------------------------------------
*------------------------------------------------------------------------*/
// frac is a 24-bit mantissa, right shifted by 9
// In the normal case, (24-9) = 15 are set
// 右移9位,还剩14位中间结果,并保留低9位是否为非零的进位
// 个人理解,bit15预留,检测舍入时,会不会进位到2,需要指数+1的情况
// bit14给到normal的隐含1,subnormal的隐含0,
// bit0-bit13 移位剩下的14位尾数待处理
frac16 = frac>>9 | ((frac & 0x1FF) != 0);
// 0 case
if ( ! (exp | frac16) ) {
uiZ = packToBF16UI( sign, 0, 0 );
goto uiZ;
}
/*------------------------------------------------------------------------
*------------------------------------------------------------------------*/
// softfloat_roundPackToBF16 exponent argument (2nd argument)
// must correspond to the exponent of fracIn[13] bits
// (fracIn is the 3rd and last argument)
// 如果是normal,则需要补齐隐含的1到bit14,subnormal补0
uint_fast32_t mask = exp ? 0x4000 : 0x0; // implicit one mask added if input is a normal number
// exponent for the lowest normal and largest subnormal should be equal
// but is not in IEEE encoding so mantissa must be partially normalized
// (by one bit) for subnormal numbers. Such that (exp - 1) corresponds
// to the exponent of frac16[13]
// 若f32为subnormal,需对齐bf16的normal格式(指数位-1,尾数左移1),统一给softfloat_roundPackToBF16处理
frac16 = frac16 << (exp ? 0 : 1);
return softfloat_roundPackToBF16( sign, exp - 1, frac16 | mask );
uiZ:
uZ.ui = uiZ;
return uZ.f;
}


/** sig last significant bit is sig[7], the 7 LSBs will be used for rounding */
bfloat16_t
softfloat_roundPackToBF16( bool sign, int_fast16_t exp, uint_fast16_t sig )
{
uint_fast8_t roundingMode;
bool roundNearEven;
uint_fast8_t roundIncrement, roundBits;
bool isTiny;
uint_fast16_t uiZ;
union ui16_bf16 uZ;
/*------------------------------------------------------------------------
*------------------------------------------------------------------------*/
// 如果是RNTE,因为当前还有14位数,bf16仅有7位尾数,需要舍弃低7位尾数
// 7bit的中间值是0x40,RNTE规则:若被截断的位(roundBits)大于等于中间值则进位
// 若为中间值(roundBits == 0x40),根据保留位是否为偶数调整,
// 后面sig &= ~(uint_fast16_t)(!(roundBits ^ 0x40) & roundNearEven);为roundBits == 0x40时,导致的进位来回退最低位,强制结果为偶数
roundingMode = softfloat_roundingMode;
roundNearEven = (roundingMode == softfloat_round_near_even);
roundIncrement = 0x40;
if ( ! roundNearEven && (roundingMode != softfloat_round_near_maxMag) ) {
roundIncrement =
(roundingMode
== (sign ? softfloat_round_min : softfloat_round_max))
? 0x7F
: 0;
}
roundBits = sig & 0x7F;
/*------------------------------------------------------------------------
*------------------------------------------------------------------------*/
if ( 0xFD <= (unsigned int) exp ) {
if ( exp < 0 ) {
/*----------------------------------------------------------------
*----------------------------------------------------------------*/
isTiny =
(softfloat_detectTininess == softfloat_tininess_beforeRounding)
|| (exp < -1) || (sig + roundIncrement < 0x8000);
sig = softfloat_shiftRightJam32( sig, -exp );
exp = 0;
roundBits = sig & 0x7F;
if ( isTiny && roundBits ) {
softfloat_raiseFlags( softfloat_flag_underflow );
}
} else if ( (0xFD < exp) || (0x8000 <= sig + roundIncrement) ) {
/*----------------------------------------------------------------
*----------------------------------------------------------------*/
softfloat_raiseFlags(
softfloat_flag_overflow | softfloat_flag_inexact );
uiZ = packToBF16UI( sign, 0xFF, 0 ) - ! roundIncrement;
goto uiZ;
}
}
/*------------------------------------------------------------------------
*------------------------------------------------------------------------*/
sig = (sig + roundIncrement)>>7;
if ( roundBits ) {
softfloat_exceptionFlags |= softfloat_flag_inexact;
#ifdef SOFTFLOAT_ROUND_ODD
if ( roundingMode == softfloat_round_odd ) {
sig |= 1;
goto packReturn;
}
#endif
}
sig &= ~(uint_fast16_t) (! (roundBits ^ 0x40) & roundNearEven);
if ( ! sig ) exp = 0;
/*------------------------------------------------------------------------
*------------------------------------------------------------------------*/
packReturn:
uiZ = packToBF16UI( sign, exp, sig );
uiZ:
uZ.ui = uiZ;
return uZ.f;
}
在SoftFloat库中,softfloat_roundPackToBF16函数处理多种舍入模式,每种模式对应不同的舍入增量(roundIncrement)和后续操作。以下是主要舍入规则的分析:
1. 舍入到最近偶数(Round to Nearest, Ties to Even)
- 模式标识:
softfloat_round_near_even roundIncrement值:0x40- 处理逻辑:
- 添加中间值(
0x40),若被截断的位(roundBits)大于等于中间值则进位。 - 若为中间值(
roundBits == 0x40),根据保留位是否为偶数调整:sig &= ~(uint_fast16_t)(!(roundBits ^ 0x40) & roundNearEven);
- 添加中间值(
2. 向零舍入(Round toward Zero)
- 模式标识:
softfloat_round_minMag(或其他等效模式) roundIncrement值:0- 处理逻辑:
- 直接截断多余位,不进行任何进位。
- 示例:若
roundBits = 0x7F(最大值),仍直接丢弃。
3. 向正无穷舍入(Round toward Positive Infinity)
- 模式标识:
softfloat_round_max roundIncrement值:符号相关:- 若数为正(
sign == false):roundIncrement = 0x7F(强制进位)。 - 若数为负(
sign == true):roundIncrement = 0(直接截断)。
- 若数为正(
- 处理逻辑:
- 正数时,任何非零舍入位均导致进位(
+0x7F确保进位)。 - 负数时,直接截断(向下舍入)。
- 正数时,任何非零舍入位均导致进位(
4. 向负无穷舍入(Round toward Negative Infinity)
- 模式标识:
softfloat_round_min roundIncrement值:符号相关:- 若数为负(
sign == true):roundIncrement = 0x7F(强制进位以向下舍入)。 - 若数为正(
sign == false):roundIncrement = 0(直接截断)。
- 若数为负(
- 处理逻辑:
- 负数时,任何非零舍入位均导致进位(使数值更小)。
- 正数时,直接截断(向下舍入)。
5. 就近舍入,中间值远离零(Round to Nearest, Ties Away from Zero)
- 模式标识:
softfloat_round_near_maxMag roundIncrement值:0x40(与RNTE相同,但处理不同)- 处理逻辑:
- 不进入符号相关分支,直接使用
roundIncrement = 0x40。 - 中间值(
roundBits == 0x40)时,直接进位(不调整奇偶性)。 - 示例:
0x0140→sig + 0x40 = 0x0180→ 右移7位得0x03,保留该结果。
- 不进入符号相关分支,直接使用
6. 奇进舍入(Round Odd)
- 模式标识:
softfloat_round_odd(需启用宏SOFTFLOAT_ROUND_ODD) - 处理逻辑:
- 强制将结果的最低位设为1,使其为奇数:
if (roundingMode == softfloat_round_odd) { sig |= 1; goto packReturn; } - 适用于需要避免舍入误差累积的场景。
- 强制将结果的最低位设为1,使其为奇数:
关键代码逻辑总结
// 根据舍入模式设置增量
roundIncrement = 0x40; // 默认:RNTE或RNTZ
if (!roundNearEven && (roundingMode != softfloat_round_near_maxMag)) {
// 判断是否为RP或RM模式
bool isDirectedMode = (roundingMode == (sign ? softfloat_round_min : softfloat_round_max));
roundIncrement = isDirectedMode ? 0x7F : 0;
}
// 添加增量并右移
sig = (sig + roundIncrement) >> 7;
// 处理中间值(仅RNTE)
if (roundBits && roundNearEven) {
sig &= ~(uint_fast16_t)(!(roundBits ^ 0x40));
}
示例对比
场景:sig = 0x0140(二进制 0000_0001_0100_0000),roundBits = 0x40
- RNTE:
sig + 0x40 = 0x0180→0x03,检查中间值后可能调整为0x02(偶数)。
- RNTZ:
sig + 0x40 = 0x0180→0x03(直接保留,远离零)。
- RP(正数):
roundIncrement = 0x7F→sig + 0x7F = 0x01BF→0x03(进位)。
- RM(负数):
roundIncrement = 0x7F→ 同上,使结果更小。
- RZ:
roundIncrement = 0→0x0140 >> 7 = 0x02(直接截断)。
总结
roundIncrement的作用:通过添加不同增量,控制是否进位。- 符号敏感模式(RP/RM):根据符号动态选择
0x7F或0,实现方向性舍入。 - 特殊处理(奇进):直接修改尾数位,确保结果为奇数。

浙公网安备 33010602011771号