如何在 C 中使用按位和逻辑运算符将无符号数四倍化
How to quadruple an unsigned number using bit-wise and logic operator in C
目标:
4x ( 4.400000095 ) = 17.60000038
- Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
- Max ops: 30
- Return bit-level equivalent of expression x + x + x + x for
- floating point argument f.
我的代码:
unsigned 4x(unsigned uf) {
unsigned expn = (uf >> 23) & 0xFF;
unsigned sign = uf & 0x80000000;
unsigned frac = uf & 0x007FFFFF;
if (expn == 255 || (expn == 0 && frac == 0))
return uf;
if (expn) {
expn << 2;
} else if (frac == 0x7FFFFF) {
frac >> 2;
expn << 2;
} else {
frac <<= 2;
}
return (sign) | (expn << 23) | (frac);
}
如您所料,我的代码不起作用。不是将输入加倍,而是将输入加倍。我不知道为什么,因为分数和指数总是右移/左移 2 而不是 1。我在 32 位机器中使用单精度浮点值。
注意
expn << 2;
不修改expn
。你可能想要
expn <<= 2;
同上
frac >> 2;
expn << 2;
但是,正如@chux 所指出的,您只需要将指数加 2,而不是将指数乘以 4。
一些未经测试的代码 - 留给 OP。 (GTG)
棘手的一点是处理当 *4 变为正规数时的次正规数。还要注意溢出到无穷大的大值。如果你想忽略次法线,只需 expn += 2
并检查溢出。
另一种方法是 expn += 2
用于正常数字。对于次正常,移动 frac <<= 2
并处理变为正常的情况。
代码大约有 30 个操作。
#include <stdint.h>
float x4(float x) {
// Use union to access the bits. Leap-of-faith here (float is 32 bits, endian)
union {
float f;
uint32_t u32;
} u;
u.f = x;
uint32_t expn = (u.u32 >> 23) & 0xFF;
uint32_t sign = u.u32 & 0x80000000;
uint32_t frac = u.u32 & 0x007FFFFF;
// Nan Inf
if (expn == 255) return u.f;
if (expn == 0) {
expn++; // Bring sub-normal into normal expo range
} else {
frac += 0x800000; // restore implied bit
}
// *4
frac <<= 2;
// normalize - this usually iterates twice, less for sub-normals
while (frac > 0xFFFFFF) {
expn++;
frac >>= 1; // 1's will not be shifted out as 2 LSB are 0 so no later rounding
}
// overflow to inf
if (expn >= 255) {
expn = 255;
frac = 0;
} else if (frac & 0x800000) {
frac ^= 0x800000; // clear implied bit
} else {
// still sub-normal
expn--; // should now be 0
}
u.u32 = sign | (expn << 23) | frac;
return u.f;
}
目标:
4x ( 4.400000095 ) = 17.60000038
- Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
- Max ops: 30
- Return bit-level equivalent of expression x + x + x + x for
- floating point argument f.
我的代码:
unsigned 4x(unsigned uf) {
unsigned expn = (uf >> 23) & 0xFF;
unsigned sign = uf & 0x80000000;
unsigned frac = uf & 0x007FFFFF;
if (expn == 255 || (expn == 0 && frac == 0))
return uf;
if (expn) {
expn << 2;
} else if (frac == 0x7FFFFF) {
frac >> 2;
expn << 2;
} else {
frac <<= 2;
}
return (sign) | (expn << 23) | (frac);
}
如您所料,我的代码不起作用。不是将输入加倍,而是将输入加倍。我不知道为什么,因为分数和指数总是右移/左移 2 而不是 1。我在 32 位机器中使用单精度浮点值。
注意
expn << 2;
不修改expn
。你可能想要
expn <<= 2;
同上
frac >> 2;
expn << 2;
但是,正如@chux 所指出的,您只需要将指数加 2,而不是将指数乘以 4。
一些未经测试的代码 - 留给 OP。 (GTG)
棘手的一点是处理当 *4 变为正规数时的次正规数。还要注意溢出到无穷大的大值。如果你想忽略次法线,只需 expn += 2
并检查溢出。
另一种方法是 expn += 2
用于正常数字。对于次正常,移动 frac <<= 2
并处理变为正常的情况。
代码大约有 30 个操作。
#include <stdint.h>
float x4(float x) {
// Use union to access the bits. Leap-of-faith here (float is 32 bits, endian)
union {
float f;
uint32_t u32;
} u;
u.f = x;
uint32_t expn = (u.u32 >> 23) & 0xFF;
uint32_t sign = u.u32 & 0x80000000;
uint32_t frac = u.u32 & 0x007FFFFF;
// Nan Inf
if (expn == 255) return u.f;
if (expn == 0) {
expn++; // Bring sub-normal into normal expo range
} else {
frac += 0x800000; // restore implied bit
}
// *4
frac <<= 2;
// normalize - this usually iterates twice, less for sub-normals
while (frac > 0xFFFFFF) {
expn++;
frac >>= 1; // 1's will not be shifted out as 2 LSB are 0 so no later rounding
}
// overflow to inf
if (expn >= 255) {
expn = 255;
frac = 0;
} else if (frac & 0x800000) {
frac ^= 0x800000; // clear implied bit
} else {
// still sub-normal
expn--; // should now be 0
}
u.u32 = sign | (expn << 23) | frac;
return u.f;
}