C语言中的双精度变量数据类型的算术位移

7

我正在尝试在C语言中进行双精度数据类型的算术位移。我想知道这是否是正确的方法:

注意:firdelay[][]在主函数中声明为 double firdelay[8][12]

void function1(double firdelay[][12]) {
    int * shiftptr;

    // Cast address of element of 2D matrix (type double) to integer pointer
    *shiftptr = (int *) (&firdelay[0][5]); 

    // Dereference integer pointer and shift right by 12 bits
    *shiftptr >>= 12; 
}

7
您希望通过对双精度浮点数进行位移来实现什么目的? - Praetorian
我正在将一个Simulink模型转换为C语言,在该模型中,使用Shift Arithmetic块将double类型向右算术移位12位。因此,我需要在C语言中执行相同的操作。 - Veridian
1
你能指出一些描述该模型的链接吗?我真的怀疑通过位移双精度浮点数可以得到有意义的东西。 - Alexandre C.
这个模型是数字 --> 转换为双精度浮点数 --> 右移12位 - Veridian
6个回答

6

位移浮点数据类型不会给你想要的结果。

在Simulink中,位移算术块仅对整数数据类型进行比特位移。如果您使用浮点数类型,则将输入信号除以掩码对话框中指定的要移位的位数的2^N。

编辑:
由于您没有执行任何浮点数运算的能力,因此您的选项是:

  • 了解浮点单精度数字的布局,然后找出如何按位操作它以实现除法。
  • 将您正在移植的任何算法转换为使用固定点数据类型而不是浮点数

我建议选择选项2,比选项1容易得多。


所以你的意思是我应该除以2^N而不是位移吗?嗯...在C代码完成后,我需要将这个模型转换为汇编,你有什么想法如何将该方法扩展到汇编中?(我的汇编中每个数据字为16位) - Veridian
@starbox:要将代码转换为汇编语言,请让编译器帮你完成(使用gcc的“-S”开关)。 - Alexandre C.
@starbox 你需要查找处理器文档以获取汇编浮点除法指令,然后使用它(如果我要猜的话,类似于 fdiv)。 - Praetorian
@starbox 然后祝你好运!看看我的编辑。 - Praetorian
要将浮点数除以2^n,只需将其指数减少n,当它下溢时需要进行一些特殊处理。 - starblue
显示剩余6条评论

4

将浮点数据类型(重新解释为int)进行位移操作会得到无意义的结果(请看二进制表示的图表这里以了解原因)。

如果要乘除2的幂次方,则应该明确执行此操作。


我并没有将数据类型转换为int,我只是将地址转换为整数指针。根据这个网站:http://www.cs.umd.edu/class/spring2003/cmsc311/Notes/BitOp/bitshift.html 这就是你应该这样做的方式。 - Veridian
@starbox:作者是正确的,这确实是如何对浮点数据类型的二进制内容进行位移操作。但是,该操作不会给出有意义的结果。 - Oliver Charlesworth
3
@starbox: 写这个链接的人应该被枪毙。不能保证floatint有相同的大小,也没有办法保证结果应该是什么。不要对浮点数进行位移操作,更重要的是,不要使用那些类型转换的技巧,它们的行为是未定义的。 - Alexandre C.
1
@starbox:它只是将符号位移入指数中,将指数位移入尾数中,并丢弃一些尾数位。这可能不是你想要的! - Oliver Charlesworth
1
通过右移,您只需将符号位向指数位移动,将指数位向分数位移动(这是浮点数的布局)。因此,结果将毫无意义。请查看下面我的答案,了解为什么在Simulink中可以使用此方法的解释。 - Praetorian
显示剩余5条评论

1
根据措辞不清且非常不明确的文档,在Simulink中,“位移”似乎需要两个浮点值作为参数,并具有将浮点值乘以2的参数差次方的效果。
您可以使用ldexp(double_number, bits_to_pseudo_shift)来获得此行为。函数ldexp位于<math.h>中。

我同意,这份文档太糟糕了。 - Veridian

0

没有正确的方法来做这件事。 << 的两个操作数必须是某种整数类型。

你正在将一个 double 对象解释为一个 int 对象(称为“类型转换”),然后移位得到结果的 int 值。即使 doubleint 恰好是相同大小,这也很不可能有任何有用的作用。(即使它有用,移位无符号值比移位有符号值更有意义)。


0
有一种方法可以实现这个目标:只需将n添加到双精度位表示的指数部分即可。使用“reinterpret”或按位转换(例如使用联合)将您的双精度转换为长整型。从52到63提取位(11位),然后添加您的移位并将结果放回指数中。您应该考虑双精度的特殊值(+无穷大,NaN或零)。
double operator_shift_left(double a,int n)
{
    union 
    {
        long long l;
        double d;
    } r;
    r.d=a;
    switch(r.l)
    {
        case 0x0000000000000000: // 0
        case 0x8000000000000000: // -0
        case 0x7FF0000000000000: // pos infnity
        case 0xFFF0000000000000: // neg infnity
        case 0x7FF0000000000001: // Nan
        case 0x7FF8000000000001: // Nan
        case 0x7FFFFFFFFFFFFFFF: // Nan
            return a;
    }
    int nexp=(((r.l>>52)&0x7FF)+n); // new exponent
    if (nexp<0) // underflow 
    {
        r.l=r.l &  0x8000000000000000;
        // returns +/- 0
        return r.d;
    }
    if (nexp>2047) // overflow
    {
        r.l=(r.l & 0x8000000000000000)| 0x7FF0000000000000;
        // returns +/- infinity
        return r.d;
    }
    // returns the number with the new exponant

    r.l=(r.l & 0x800FFFFFFFFFFFFF)|(((long long)nexp)<<52); 
    return r.d;


}

(可能有一些x64处理器指令可以完成这个操作?)


0

这个的一个潜在用例是捕获尾数位、指数位和符号位(如果有兴趣的话)。为此,您可以使用一个联合:

union doubleBits {
    double d;
    long l;
};

你可以将你的双精度数设置到联合体中:

union doubleBits myUnion;
myUnion.d = myDouble;

在提取位之后,对联合体的长部分进行位移,如下所示:

myUnion.l >>= 1;

由于双精度浮点数的每个部分的位数是固定的,因此这是一种提取底层位表示的方法。这是一个使用案例,可能希望获取原始底层位。我不熟悉Simulink,但如果这可能是双精度浮点数在第一次移位时的原因,那么这可能是在C中实现该行为的一种方式。它始终是12位的事实使我想到了其他方面,但以防万一,我认为值得为其他遇到这个问题的人指出。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接