我需要编写一个程序来模拟浮点数乘法。对于这个程序,我们假设单精度浮点数存储在
unsigned long a
中。我需要使用以下运算符将存储在a
中的数字乘以2:<< >> | & ~ ^
我理解这些运算符的功能,但是我对如何实现此操作的逻辑感到困惑。任何帮助都将不胜感激。unsigned long a
中。我需要使用以下运算符将存储在a
中的数字乘以2:<< >> | & ~ ^
我理解这些运算符的功能,但是我对如何实现此操作的逻辑感到困惑。任何帮助都将不胜感激。必须使用以下运算符之一将a中存储的数字乘以2:<< >> | & ~ ^
由于我们有一个无符号长整型来模拟具有单精度的浮点值,因此我们应该处理所有可以模拟的情况。ref
首先让我们假设浮点数被编码为binary32,而unsigned
是32位的。C语言不需要这两个条件。
首先隔离指数以处理float
子组:次正常、正常、无穷大和NAN。
下面是一些经过轻微测试的代码-我稍后会进行审核,现在请将其视为伪代码模板。
#define FLT_SIGN_MASK 0x80000000u
#define FLT_MANT_MASK 0x007FFFFFu
#define FLT_EXPO_MASK 0x7F800000u
#define FLT_EXPO_LESSTHAN_MAXLVAUE(e) ((~(e)) & FLT_EXPO_MASK)
#define FLT_EXPO_MAX FLT_EXPO_MASK
#define FLT_EXPO_LSBit 0x00800000u
unsigned increment_expo(unsigned a) {
unsigned carry = FLT_EXPO_LSBit;
do {
unsigned sum = a ^ carry;
carry = (a & carry) << 1;
a = sum;
} while (carry);
return a;
}
unsigned float_x2_simulated(unsigned x) {
unsigned expo = x & FLT_EXPO_MASK;
if (expo) { // x is a normal, infinity or NaN
if (FLT_EXPO_LESSTHAN_MAXLVAUE(expo)) { // x is a normal
expo = increment_expo(expo); // Double the number
if (FLT_EXPO_LESSTHAN_MAXLVAUE(expo)) { // no overflow
return (x & (FLT_SIGN_MASK | FLT_MANT_MASK)) | expo;
}
return (x & FLT_SIGN_MASK) | FLT_EXPO_MAX;
}
// x is an infinity or NaN
return x;
}
// x is a sub-normal
unsigned m = (x & FLT_MANT_MASK) << 1; // Double the value
if (m & FLT_SIGN_MASK) {
// Doubling caused sub-normal to become normal
// Special code not needed here and the "carry" becomes the 1 exponent.
}
return (x & FLT_SIGN_MASK) | m;
}
fpmul_by_2()
实现了所需的功能,假设 'unsigned long' 是 32 位整数类型,'float' 是映射到 IEEE-754 'binary32' 的 32 位浮点类型。进一步假设我们要模仿禁用异常的 IEEE-754 乘法,生成标准规定的屏蔽响应。此外使用了两个帮助函数,分别实现了32位整数加法和等式比较。加法基于二进制加法中的和与进位比特的定义 (详见 this previous question),而等式比较则利用了 (a^b) == 0
等价于 a == b
的事实。#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// assumptions:
// 'unsigned long' is a 32-bit type
// 'float' maps to IEEE-754 'binary32'. Exceptions are disabled
// add using definition of sum and carry bits in binary addition
unsigned long add (unsigned long a, unsigned long b)
{
unsigned long sum, carry;
carry = b;
do {
sum = a ^ carry;
carry = (a & carry) << 1;
a = sum;
} while (carry);
return sum;
}
// return 1 if a == b, else 0
int eq (unsigned long a, unsigned long b)
{
unsigned long t = a ^ b;
// OR all bits into lsb
t = t | (t >> 16);
t = t | (t >> 8);
t = t | (t >> 4);
t = t | (t >> 2);
t = t | (t >> 1);
return ~t & 1;
}
// compute 2.0f * a
unsigned long fpmul_by_2 (unsigned long a)
{
unsigned long expo_mask = 0x7f800000UL;
unsigned long expo_lsb = 0x00800000UL;
unsigned long qnan_mark = 0x00400000UL;
unsigned long sign_mask = 0x80000000UL;
unsigned long zero = 0x00000000UL;
unsigned long r;
if (eq (a & expo_mask, zero)) { // subnormal or zero
r = a << 1; // double significand
} else if (eq (a & expo_mask, expo_mask)) { // INF, NaNs
if (eq (a & ~sign_mask, expo_mask)) { // INF
r = a;
} else { // NaN
r = a | qnan_mark; // quieten SNaNs
}
} else { // normal
r = add (a, expo_lsb); // double by bumping exponent
if (eq (r & expo_mask, expo_mask)) { // overflow
r = expo_mask;
}
}
return r | (a & sign_mask); // result has sign of argument
}
float uint_as_float (unsigned long a)
{
float r;
memcpy (&r, &a, sizeof r);
return r;
}
unsigned long float_as_uint (float a)
{
unsigned long r;
memcpy (&r, &a, sizeof r);
return r;
}
int main (void)
{
unsigned long res, ref, a = 0;
do {
res = fpmul_by_2 (a);
ref = float_as_uint (2.0f * uint_as_float (a));
if (res != ref) {
printf ("error: a=%08lx res=%08lx ref=%08lx\n", a, res, ref);
return EXIT_FAILURE;
}
a++;
} while (a);
printf ("test passed\n");
return EXIT_SUCCESS;
}
+
运算符的简单代码。它并不打算涵盖浮点数处理的所有方面。此解决方案向您展示,将单精度浮点数的指数(位23-29,30是指数符号)加1,即可获得乘以2的结果。#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
int main()
{
float f = 23.45F;
uint32_t *i=(uint32_t *)(&f);
uint32_t app;
printf("%08X %f\n",*i,f);
app = *i & (0xC0000000); // copies bits 31 and 30
*i += (1U<<23);
*i &= ~(0xC0000000); // leave bits 31 and 30
*i |= app; // set original bits 31 and 30
printf("%08X %f\n",*i,f);
return 0;
}
另请参阅: 维基百科单精度浮点数
int exp = ... ; switch (exp) { ... case 23: exp = 24; break; case 24: exp = 25; break; ... } ...
或等效的代码,这太奇怪了^^ - brunoFLT_MAX
、NAN、次正规化值以及可能的0值时会失败。还存在抗锯齿问题和可移植性差的情况。 - chux - Reinstate Monica#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
int main()
{
float f = -23.45F;
uint32_t *i=(uint32_t *)(&f);
uint32_t sgn;
uint32_t c,sc;
printf("%08X %f\n",*i,f);
sgn = *i & (0xC0000000); // copies bits 31 and 30
c = *i & (1U<<23);
*i ^= (1U<<23);
while(c)
{
sc = c << 1;
c = *i & sc;
*i ^= sc;
};
if (sgn != *i & (0xC0000000)) {
puts("Exponent overflow");
}
printf("%08X %f\n",*i,f);
return 0;
}
参见: 维基百科 单精度浮点数
float
值吗?如果不是,那么代码需要模拟哪个子集的float
值? - chux - Reinstate Monicaunsigned long
来模拟一个只有一位精度的浮点值,那么我们应该处理所有可以模拟的情况。 - user10992941