【问题标题】:Bit shifting for fixed point arithmetic on float numbers in CC中浮点数的定点运算的位移位
【发布时间】:2012-05-30 15:28:53
【问题描述】:

我编写了以下测试代码来检查定点算术和位移。

void main(){
    float x = 2;
    float y = 3;
    float z = 1;
    unsigned int * px = (unsigned int *) (& x);
    unsigned int * py = (unsigned int *) (& y);
    unsigned int * pz = (unsigned int *) (& z);
    *px <<= 1;
    *py <<= 1;
    *pz <<= 1;
    *pz =*px + *py;
    *px >>= 1;
    *py >>= 1;
    *pz >>= 1;
    printf("%f %f %f\n",x,y,z);
  }

结果是 2.000000 3.000000 0.000000

为什么最后一个数字是 0?我期待看到一个 5.000000 我想使用某种定点算法来绕过在图像处理应用程序上使用浮点数。将浮点数组转换为整数的最佳/最简单/最有效的方法是什么?上面的“欺骗编译器”是一种强大的解决方法吗?有什么建议吗?

【问题讨论】:

  • 当您将 *px 和 other 移动一位时,您只擦除了符号位,但没有擦除指数。有一个bit format of IEEE float

标签: c floating-point bit-shift fixed-point


【解决方案1】:

如果你想使用定点,不要使用类型'float'或'double',因为它们有内部结构。浮点数和双精度数具有特定的符号位;一些位用于指数,一些位用于尾数(看看彩色图像here);所以它们本质上是浮点数。

您应该手动编程定点,以整数类型存储数据,或者使用一些定点库(或语言扩展)。

有在 GCC 中实现的浮点扩展的描述:http://gcc.gnu.org/onlinedocs/gcc/Fixed_002dPoint.html

C:http://www.eetimes.com/discussion/other/4024639/Fixed-point-math-in-C 有一些基于 MACRO 的定点手动实现

【讨论】:

  • 不幸的是,这不是一个选择。我有一个使用浮点数在 ARM 处理器上运行的应用程序,我必须将内容发送到 DSP 进行处理。 DSP 没有浮点单元,所以在发送数据之前,我必须将它们转换为定点。没有移植浮点扩展。
  • @user1410966,您可以在 ARM 上进行浮点计算,但在将数据发送到 DSP 之前,您应该手动将浮点转换为固定。 DSP上可以使用哪些固定格式?
  • 有一种转换方式:linkquote: "double f = 1.2345; int n; n=(int)(f*65536);"(如果需要16:16定点格式)。
【解决方案2】:

你所做的是对数字的残忍。

首先,您将值分配给浮点变量。它们的存储方式取决于系统,但通常使用IEEE 754 format。所以你的变量内部看起来像

x = 2.0 = 1 * 2^1   : sign = 0, mantissa = 1,   exponent = 1 -> 0 10000000 00000000000000000000000 = 0x40000000
y = 3.0 = 1.5 * 2^1 : sign = 0, mantissa = 1.5, exponent = 1 -> 0 10000000 10000000000000000000000 = 0x40400000
z = 1.0 = 1 * 2^0   : sign = 0, mantissa = 1,   exponent = 0 -> 0 01111111 00000000000000000000000 = 0x3F800000

如果您对这些数字进行一些位移操作,就会混淆符号、指数和尾数之间的边界,因此任何事情都可能发生、可能发生并且将会发生。

在你的情况下:

  • 您的 2.0 变为 0x80000000,导致 -0.0,
  • 你的 3.0 变成 0x80800000,导致 -1.1754943508222875e-38,
  • 您的 1.0 变为 0x7F000000,结果为 1.7014118346046923e+38。

后者你通过添加 -0.0 和 -1.1754943508222875e-38 来丢失,它变成了后者,即 0x80800000,它应该是,在再次 >> 将其 1 3.0 之后。我不知道为什么不是,可能是我这里弄错了。

剩下的就是你不能对浮点数进行位移以获得可靠的结果。

我会考虑将它们转换为 ARM 上的整数或其他定点,然后按原样通过线路发送。

【讨论】:

  • 完美!这正是我需要开始的信息。将它们转换为精度较低的整数的最安全方法是什么?
  • 取你能想到的最大数(例如 10),然后求下一个最大的 2 次幂(16)。然后选择一个整数类型(例如 uint16)并通过应用因子 4096 使值 16 等于“整数空间”中的 65536。这样,对于 1,您将获得 4096,对于 0.25,您将获得 1024,对于任何其他值在不确定的精度下,您会得到一个奇数。请注意,例如 0.1 在表示为浮点数时非常奇数,因此它也会在整数空间中为您提供奇数。
【解决方案3】:

您的编译器很可能对floats 使用IEEE 754 格式,按位计算,如下所示:

SEEEEEEEEFFFFFFFFFFFFFFFFFFFFFFF
^ bit 31                       ^ bit 0

S 是符号位 s = 1 表示数字为负数。

E 位是指数。有 8 个指数位,范围为 0 - 255 指数有偏差 - 您需要减去 127 才能获得真正的指数。

F 位是小数部分,但是,您需要想象前面有一个不可见的 1,因此小数始终是 1.something,而您看到的只是二进制小数位。

数字 2 是 1 x 21 = 1 x 2128 - 127 所以编码为

01000000000000000000000000000000

因此,如果您使用位移位将其向右移动,则会得到

10000000000000000000000000000000

按照惯例,在 IEEE754 中是 -0,因此您的班次不是将您的数字乘以 2,而是将其设为零。

数字 3 是 [1 + 0.5] x 2128 - 127

表示为

01000000010000000000000000000000

向左移动给你

10000000100000000000000000000000

这是 -1 x 2-126 或一些非常小的数字。

你可以对 z 做同样​​的事情,但你可能会认为移位只会搞砸浮点数。

【讨论】:

    【解决方案4】:

    固定点不能那样工作。你想做的是这样的:

    void main(){
        // initing 8bit fixed point numbers
        unsigned int x = 2 << 8;
        unsigned int y = 3 << 8;
        unsigned int z = 1 << 8;
    
        // adding two numbers
        unsigned int a = x + y;
    
        // multiplying two numbers with fixed point adjustment
        unsigned int b = (x * y) >> 8;
    
        // use numbers
        printf("%d %d\n", a >> 8, b >> 8);
      }
    

    【讨论】:

    • 正确。对于整数。但我的问题是如何使用浮动来做到这一点。
    • 您不能对浮点表示的数据进行定点数学运算。您可以将浮点数转换为定点整数并执行我上面写的操作,或者您可以模拟浮点算术,但这可能很复杂。但是,您的原始代码不会以任何有意义的方式处理浮点数据。
    • @user1410966:您应该阅读更多内容以更好地理解定点数、浮点数和二进制有理数。每种定点类型都有固定的小数点位置,只需将浮点数的尾数移动到正确的位置,使整数和小数部分都有正确的值
    猜你喜欢
    • 1970-01-01
    • 2015-05-25
    • 1970-01-01
    • 2011-07-17
    • 1970-01-01
    • 2017-06-17
    • 2019-07-25
    • 2014-10-01
    • 1970-01-01
    相关资源
    最近更新 更多