将二进制(整数和分数)从 VHDL 转换为十进制,C 代码中的负值

Convert binary ( integer and fraction) from VHDL to decimal, negative value in C code

我有一个 14 位数据,它是从 FPGA 以 vhdl 形式提供的,NIos II 处理器从 FPGA 读取 14 位数据并执行一些处理任务,其中 Nios II 系统是用 C 代码编程的

14 位数据可以是正数、零或负数。在Altera编译器中,我只能定义数据为8,16或32。所以我定义为16位数据。

首先,我需要检查数据是否为负数,如果为负数,我需要将前两个 MSB 填充为位“1”,以便系统将其检测为负值而不是正值。

其次,我需要将此二进制表示的实数值计算为整数和小数的十进制值。

我从这个 link (Correct algorithm to convert binary floating point "1101.11" into decimal (13.75)?) 中了解到我可以将二进制(由整数和分数组成)转换为十进制值。

需要说明的是,我可以使用引用自 link (Correct algorithm to convert binary floating point "1101.11" into decimal (13.75)?) 的代码,转载如下:

#include <stdio.h>
#include <math.h>

double convert(const char binary[]){
  int bi,i;
  int len = 0;
  int dot = -1;
  double result = 0;

  for(bi = 0; binary[bi] != '[=10=]'; bi++){
    if(binary[bi] == '.'){
      dot = bi;
    }
    len++;
  }
  if(dot == -1)
    dot=len;

  for(i = dot; i >= 0 ; i--){
    if (binary[i] == '1'){
      result += (double) pow(2,(dot-i-1));
    }
  }
  for(i=dot; binary[i] != '[=10=]'; i++){
    if (binary[i] == '1'){
      result += 1.0/(double) pow(2.0,(double)(i-dot));
    }
  }
  return result;
}

int main()
{
   char  bin[] = "1101.11";
   char  bin1[] = "1101";
   char  bin2[] = "1101.";
   char  bin3[] = ".11";

   printf("%s -> %f\n",bin, convert(bin)); 
   printf("%s -> %f\n",bin1, convert(bin1)); 
   printf("%s -> %f\n",bin2, convert(bin2)); 
   printf("%s -> %f\n",bin3, convert(bin3)); 

   return 0;
}

我想知道这段代码是否可以用来检查负值?我确实尝试使用 11111101.11 的二进制字符串,它给出了 253.75 的输出...

我有两个问题:

  1. 我需要做哪些修改才能读取负值?

我知道我可以做位移(如下)来检查msb是否为1,如果是1,我知道它是负值...

if (14bit_data & 0x2000) //if true, it is negative value

问题是,因为它涉及分数部分(但不仅是整数),如果该方法仍然有效,我有点困惑...

  1. 如果二进制数本来就不是字符串格式,有什么办法可以转换成字符串吗?二进制数最初是从用 VHDL 编写的 fpga 块提供的,比如 14 位,以 msb 为符号位,接下来的 6 位是整数的大小,最后 6 位是小数部分的大小。我需要 Altera Nios II 处理器的 C 代码中的十进制值。

我认为这应该可行:

float convert14BitsToFloat(int16_t in)
{
    /* Sign-extend in, since it is 14 bits */
    if (in & 0x2000) in |= 0xC000;

    /* convert to float with 6 decimal places (64 = 2^6) */
    return (float)in / 64.0f;
}

要将任何数字转换为字符串,我会使用 sprintf。请注意,它可能会显着增加您的应用程序的大小。如果你不需要float和what to keep一个小应用,你应该自己制作转换函数。

好的,所以我关注的是你想重用你在问题开头提到的算法,并假设你的签名数的二进制表示是 Two's complement 但根据您的评论,我不太确定您的输入是否与算法使用的输入相同

首先填充 2 MSB 以具有 16 位表示

16bit_data = (14_bit_data & 0x2000) ? ( 14_bit_data | 0xC000) : 14_bit_data ;

如果值为正则值将保持不变,如果为负则这将是 16 位上正确的二进制补码表示。

对于小数部分,与您在问题中提到的算法相比,一切都相同。
对于整数部分,除了 MSB 的处理外,其他都一样。
对于无符号数 MSB(即位 [15])表示 pow(2,15-6)(6 是小数部分的宽度),而对于二进制补码表示中的有符号数,它表示 -pow(2,15-6)意味着算法变成

/* integer part operation */    
while(p >= 1)
{
  rem = (int)fmod(p, 10);
  p = (int)(p / 10);
  dec = dec + rem * pow(2, t) * (9 != t ? 1 : -1);
  ++t;
}

如果您不想要 * 运算符,则换句话说

/* integer part operation */    
while(p >= 1)
{
  rem = (int)fmod(p, 10);
  p = (int)(p / 10);
  if( 9 != t)
  {
     dec = dec + rem * pow(2, t);
  }
  else
  {
     dec = dec - rem * pow(2, t);
  }
  ++t;
}

对于您提到的第二种算法,考虑到如果点 == 11 且 i == 0 的格式,我们处于 MSB(10 个整数位后跟点),因此代码变为

for(i = dot - 1; i >= 0 ; i--)
{
   if (binary[i] == '1')
   {
       if(11 != dot || i)
       {
           result += (double) pow(2,(dot-i-1));
       }
       else
       {
           // result -= (double) pow(2,(dot-i-1));
           // Due to your number format i == 0 and dot == 11 so
          result -= 512
       }
   }
}

警告:在 brice 算法中,输入是字符串,如“11011.101”,而根据您的描述,您输入的是整数,因此我不确定该算法是否适合您的情况