C 如何使用union(位字段)处理自定义浮点?

C 如何使用union(位字段)处理自定义浮点?,c,floating-point,unions,bit-fields,C,Floating Point,Unions,Bit Fields,我正在分析一个运行在微控制器上处理指向点的前代代码代码,但我不明白它是如何工作的。我必须知道如何将单位转换为十进制,反之亦然。然而,他所做的是为数据结构中的指数:6位和尾数:26位使用定制的位字段 typedef union { struct { #ifdef _BIG_ENDIAN unsigned int mant: 26; /* -33,554,432 to +33,554,431 */ unsigned int exp: 6; /* 10

我正在分析一个运行在微控制器上处理指向点的前代代码代码,但我不明白它是如何工作的。我必须知道如何将单位转换为十进制,反之亦然。然而,他所做的是为数据结构中的指数:6位和尾数:26位使用定制的位字段

typedef union {
  struct {
   #ifdef _BIG_ENDIAN
      unsigned int mant: 26;    /* -33,554,432 to +33,554,431 */
      unsigned int exp: 6;      /* 10^-32 to 10^+31 */
   #else
      unsigned int exp: 6;      /* 10^-32 to 10^+31 */
      unsigned int mant: 26;    /* -33,554,432 to +33,554,431 */
   #endif
 } part;
 unsigned long comp;
} DMKS;
客户端的程序逻辑如下:

客户端微控制器从服务器获取数据,并将值设置为无符号长comp

调用M_to_,将DMKS值转换为micro值

M_to_如下所示:

long M_to_u(DMKS dmks_val)
{

register unsigned int exp;
long    retval;

UARTprintf("before x= %x\n",  (dmks_val.comp));//First print of comp

retval = (long) (dmks_val.part.mant);    

UARTprintf("retval x= %x\n", (long) (dmks_val.part.mant));//second print of mantissa

if (retval & 0xfe000000)
{
    retval |= 0xfe000000;
}

exp = dmks_val.part.exp;

UARTprintf("exp = %d\n", (long) (dmks_val.part.exp));//Third print of exponent
//UARTprintf("exp x= %x\n", (long) (dmks_val.part.exp));


switch(exp) {
  case 58:
retval /= 1000000L;
break;
  case 59:
retval /= 100000L;
break;
  case 60:
retval /= 10000L;
break;
  case 61:
retval /= 1000L;
break;
  case 62:
retval /= 100L;
break;
  case 63:
retval /= 10L;
break;
  case 0:
break;
  case 1:
retval *= 10L;
break;
  case 2:
retval *= 100L;
break;
  case 3:
retval *= 1000L;
break;
  case 4:
retval *= 10000L;
break;
  default:
break;
}

return(retval);
}

打印值

-IEEE 754标准指定了二进制32格式示例

-此代码中的自定义浮点=111101000010000000111101

尾数指数 +--+--+--+--+--+--+--+--+ | 11110100001001000000 | 111101 | +--+--+--+--+--+--+--+--+

第一个UARTPrint::x=3d0903d之前,这是从服务器发送的联合中的comp的纯值,bin格式=111101000010000000111101

尾数字段的第二次打印::retval x=f4240,从尾数的位字段中提取,bin格式=1111010000100000000

第三次打印::exp=61,从指数的位字段中提取,bin格式=111101

问题是:

与IEEE 754标准规定的二进制32 exp位:23~30和尾数位:0~22不同,自定义位字段用于此代码中的浮点

一,。事情是如何运作的? -这些代码使用exp位:0~5和尾数位:6~30,因此客户机/服务器需要进行操作 位的顺序

二,。为什么要除以1000L? -打印出exp值=61,所以进入情况61:,但指数61是如何连接到除以1000L的

谢谢 -Jin

您的前任所做的可能会起作用,但这是一种不可靠且危险的方法-结构中位字段的序列化顺序由实现定义,并且不要求它与CPU用于整数的endianness一致,顺便说一句,不一定与用于硬件浮点的endianness相同!,声明的顺序,或者其他任何东西

由于您正在以这种自定义格式接收来自微控制器的无符号long,我将记录字段的顺序-看起来尾数在高位,指数在低位,每个字段中的位顺序与CPU的位顺序一致;在没有文档或测试的情况下,这些都是不可靠的,然后使用显式位屏蔽来提取字段,例如

unsigned long mantissa = (dmks_val & 0xFFFFFFC0) >> 6;
unsigned long exponent = (dmks_val & 0x0000003F) >> 0;
其中,dmks_val现在是一个未签名的long,而不是union。联盟应该用火焰和剑从你的代码库中清除


我不能帮你回答第二个问题。只有了解这种奇怪的浮点格式的人才能帮助你解决问题2。顺便说一句,您最好找到符号位。

很抱歉,我刚刚在联合定义中删除了一些代码,因为我使用的是小端元。我添加了那些丢失的代码。谢天谢地,丢失的代码使得一个非常常见但完全不正确的假设,即字段的顺序与CPU的endianness相关。我说的一切都是真的,谢谢你的回答。据我所知,这些代码在客户机/服务器之间已经运行了很长一段时间,并且没有指定负责服务器端的人员。我认为这种自定义格式对于服务器来说是可以理解的。您不必更改wire格式,但您认为处理它的客户端代码已损坏,需要进行重大修改是正确的。因此,这种格式在客户端和服务器之间紧密耦合。我不确定我能不能干掉工会。老实说,我不理解你提到的>>5和>>0。谢谢!好的,在我看来,这是一种浮点格式,用十进制而不是二进制思考。你拉取尾数,然后出于某种原因,手动决定一个十进制指数只在-6..4范围内,并使用它按10的因子进行缩放。由于您提到了一台服务器,因此您可能还具有comp别名,以便可以调用htons在网络上传送beast。这能让你动起来吗?人们想象希望,祈祷服务器理解同样的结构。我猜那个家伙不知道IEEE754是一个实际的标准。谢谢你的回复,补偿是由ntohs在调用上述函数之前设置的。至于指数,2^k−1.− 1和k=6,这给了我exp=31。所以它应该是2^31,但前一个注释是10^-32到10^+31,这是十进制基数。我不明白这是怎么改变的这就是switch语句所做的:它查看指数并按十的幂缩放尾数…但只在有限的范围内。不过,我看不到两个人的力量。我只是很密集,还是在不同的代码段中?如果是的话,也许这是一个更大的范围。我很好奇:是服务器吗
也是内部的吗?如果是这样,也许有一些有用的证据,如果不是,也许有文件。至少现在联盟是有意义的,对。switch语句也以十的幂运算,并带有手动标志。0是x1。1是x10。63 is-1 is/10。2是x100。62 is-2 is/100。以此类推,但仅从-6到4,要么因为这是x10000的长26位+13的全部,所以为39,所以为否,要么因为他假设这是最大范围。换句话说,它可能是10^-31到10^32,但是10^-6到10^4是所有实现的。哦,对不起。我认为标志部分很明显。这不是偏见;这是一个负数。一个六位负数从0 000000到31 011111,然后从32 100000到-1 111111。所以,58个无符号的是-6个有符号的111010,59个是-51111011,60个是-41111100,61个是-3111016,62个是-21111110,63个是-111111111。有道理?