2015-04-28 26 views
1
float input = whatever; 
long output = (long)(0.5f + input); 

这对使用编译器提供的浮点附加支持库在MSP430上的应用程序是低效的。将有符号单精度浮点四舍五入到最近整数的有效方法是什么?

我在想,这种特殊类型的“最接近的整数”舍入可能会有一个聪明的“窍门”,避免纯浮点加法,或许是直接“点缀”浮点表示,但我还没有找到这样的。任何人都可以提出这样一个把IEEE 754 32位浮点数舍去的技巧吗?

+0

所以你正在寻找'long float_to_long_round_nearest(float)'函数的实现,对吗?在您的平台上“长”包含多少位?转换能够正确处理哪些“float”操作数范围?应该如何处理领带案件:联系一致,还是联系起来?似乎期望的转换过程不应该涉及浮点运算,因为这些是在这个平台上模拟的?我认为它的编程语言是C? – njuffa

+1

软件浮点运行缓慢。解决缓慢软件浮点问题的唯一方法是使用硬件浮点。 –

+0

使用直接在位级表示上操作的位操作方法将IEEE-754“float”转换为“long”当然是可能的,但是有一个精确的规范会很有帮助。今天晚些时候,我应该可以为此制作一些C代码。 – njuffa

回答

3

按位操作转换非常简单,下面的C代码演示了它。根据关于MSP430上数据类型的评论,代码假定int包含16位,并且long 32位。

我们需要一种手段,尽可能有效地将float的位模式转换为unsigned long。此实现为此使用union,您的平台可能具有更高效的机器特定方式,例如,一种内在的。在最坏的情况下,使用memcpy()复制字节。

只有几种情况可以区分。我们可以检查float输入的指数字段以区分它们。如果参数太大或NaN,转换失败。一种惯例是在这种情况下返回最小的负整数操作数。如果输入小于0.5,结果为零。在消除这些特殊情况之后,我们留下那些需要一点点计算才能转换的输入。

对于足够大的参数,float始终是一个整数,在这种情况下,我们只需要将尾数模式转换为正确的位位置。如果输入太小而不是整数,我们将转换为32.32定点格式。四舍五入然后基于最重要的分数位,并且在平局的情况下,也是最不重要的整数位,因为关系必须舍入为偶数。

如果领带箱子都应该总是轮远离零,在代码舍入逻辑简化为

r = r + (t >= 0x80000000UL); 

下面是实现上面讨论的方法,将具有一个测试框架,测试此沿着float_to_long_round_nearest()彻底实施。

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

long float_to_long_round_nearest (float a) 
{ 
    volatile union { 
     float f; 
     unsigned long i; 
    } cvt; 
    unsigned long r, ia, t, expo; 

    cvt.f = a; 
    ia = cvt.i; 
    expo = (ia >> 23) & 0xff; 
    if (expo > 157) {  /* magnitude too large (>= 2**31) or NaN */ 
     r = 0x80000000UL; 
    } else if (expo < 126) { /* magnitude too small (< 0.5) */ 
     r = 0x00000000UL; 
    } else { 
     int shift = expo - 150; 
     t = (ia & 0x007fffffUL) | 0x00800000UL; 
     if (expo >= 150) { /* argument is an integer, shift left */ 
      r = t << shift; 
     } else { 
      r = t >> (-shift); 
      t = t << (32 + shift); 
      /* round to nearest or even */ 
      r = r + ((t > 0x80000000UL) | ((t == 0x80000000UL) & (r & 1))); 
     } 
     if ((long)ia < 0) { /* negate result if argument negative */ 
      r = -(long)r; 
     } 
    } 
    return (long)r; 
} 

long reference (float a) 
{ 
    return (long)rintf (a); 
} 

int main (void) 
{ 
    volatile union { 
     float f; 
     unsigned long i; 
    } arg; 
    long res, ref; 

    arg.i = 0x00000000UL; 
    do { 
     res = float_to_long_round_nearest (arg.f); 
     ref = reference (arg.f); 
     if (res != ref) { 
      printf ("arg=%08lx % 15.8e res=%08lx ref=%08lx\n", 
        arg.i, arg.f, res, ref); 
      return EXIT_FAILURE; 
     } 
     arg.i++; 
    } while (arg.i); 
    return EXIT_SUCCESS; 
} 
+0

在MSP430上,'int' /'long'具有16/32位。但是,支持''类型。 –

+0

@CL感谢您的澄清,我会相应地修复我的代码。 – njuffa

+0

这是一流的!这正是我所希望的,并且与我的应用程序使用标准C库“+ 0.5f”相比,这是一个重大改进。谢谢! – Puffin