在sigmoid传递函数中用整数替换浮点数学

时间:2014-04-06 12:02:43

标签: c math floating-point embedded

我想在这些函数中替换浮点数学而不会损失太多精度,因为我没有FPU。这可能吗?我认为逗号后的3个数字就足够了。

inline float smaller_f(float value, float bias) {
  return value < bias ? value : bias;
}

inline float pow2_f(float fVal) {
  return fVal * fVal;
}

float sigm_f(float fX, float fSlope) {
  float fVal = (180.f - smaller_f(fabs(fSlope * fX), 179.9f) ) / 180.f;
  return fVal / sqrt(1.f + pow2_f(fVal) );
}

5 个答案:

答案 0 :(得分:2)

看看最慢/最难的部分:

fVal / sqrt(1 + fVal ** 2)

这就是你需要考虑的全部内容。

http://www.wolframalpha.com/input/?i=x%2Fsqrt%281+%2B+x%5E2%29

显然你的fVal小于或等于1.

你是在x = 0到x = 1的范围内的近似值,所以这样的事情: http://www.wolframalpha.com/input/?i=expand+x%2Fsqrt%281+%2B+x%5E2%29+around+x+%3D+0.5

这可能足以满足您的需求。按下更多术语按钮一次以获得更高的准确度。

要使整数表现得像浮点,你可以使用一个简单的乘数方案,比如int = float * 10000,但是当你需要第五个幂时会产生问题 - 你会遇到溢出。最好扩展所有数字,使所有数字都小于1,然后使用小数整数数学库来乘以你的数字。

我构建的一个简单的小数库使用LONG_MAX表示1.0(精度约为9位小数),然后将其中两个相乘(使LONG_MAX * LONG_MAX = LONG_MAX)我使用了两行汇编程序。您可以访问系统中的小数数学库。

所以基本上,缩放所有内容,以便你在路上的最大值是1.0。

完成后,通过遍历一百万左右的值并将它们与浮点版本进行比较,可以非常轻松地测试此函数。

有关如何使用固定点的信息,请参阅http://gameprogrammer.com/4-fixed.html及类似页面。

答案 1 :(得分:2)

您需要定点数学库。我首选的解决方案是Anthony Williams' fixed-Point math C++ library。因为它在C ++中并且定义了具有广泛功能和运算符重载的fixed类,所以可以在很大程度上通过用float替换现有代码中的doublefixed来使用它。 。它使用int64_t作为基础整数数据类型,具有34个整数位和28个小数位(34Q28),因此适用于大约8位小数且比int32_t更宽的范围。

如果您的编译器支持C ++,您仍然可以使用基本上C子集编写代码(如果您愿意),仅使用C ++来支持此库。

在32位ARM上,这个库的执行速度比软件浮点快5倍,在性能上与ARM的VFP单元的C代码相当。

请注意,此库中的sqrt()函数对于非常小的值具有较差的精度性能,因为它在可以保留的中间计算中丢失了低阶位。可以通过将其替换为我在this question中提供的版本的代码来改进它。

毫无疑问,C库可用于定点数学,但它们缺乏简单和方便的实时数据。这个库提供的定点数据类型,这个库有一套完整的标准库数学函数等价物,而许多定点解决方案只提供基本的算术运算符。

答案 2 :(得分:0)

你可以尝试一件简单的事情,这对你来说可能不够好,但相当简单:

 unsigned int scale = 1000; /* three number after the comma */

 inline int smaller_i(int value, int bias) {
       return value < bias ? value : bias;
 }

 inline int pow2_i(int iVal) {
     return (iVal * iVal) / scale;
 }

 int sigm_i(int iX, int Slope) {
     int iVal = (180*scale - smaller_i(abs(iX) * slope, (179*scale + 9*(scale/10))) / (180*scale);
     return iVal / sqrt_i(1*scale + pow2_i(iVal));
 }

如果你有64位整数,这对你来说已经足够了。如果你只有32位,我不确定。如果只有16位,这些计算可能会溢出,所以你需要更复杂的东西。

另请注意,您需要自己编写sqrt_i

答案 3 :(得分:0)

瓶颈可能是fVal / sqrt(1.f + pow2_f(fVal) )

尝试使用Fast Inverse Square Root程序,使用整数运算产生非常准确的1.0 / sqrt(x)近似值。

答案 4 :(得分:0)

我想在Raspberry Pi 3上实现神经网络这个问题(权重在-127到127之间),我找到的最快的方法是实现为嵌套if语句的二进制搜索;很明显,if语句需要自动生成,Python才能解决。

给定C函数:

static
uint16_t sigmoid_lookup(int32_t i) {
#include "autogen_sigmoid_index.i"
}

(sigmoid_value, at_argument)的已排序Python列表,此函数创建if-else树:

def produce_c_code(numbers_list, idxs, idxe, level):
    if idxs >= idxe:
        raise RuntimeError("idxs=%d idxe=%d")

    indent= " "*level

    if idxs + 1 == idxe: # end of recursion
        yield indent + "return %d;" % numbers_list[idxs][0]
    else:
        idxm= (idxe+idxs)//2
        yield indent + "if(i>=%d)" % numbers_list[idxm][1]
        yield from produce_c_code(numbers_list, idxm, idxe, level+1)
        yield indent + "else"
        yield from produce_c_code(numbers_list, idxs, idxm, level+1)

例如

对于此号码列表:[(0, 0), (1, 9), (2, 25), (3, 41), (4, 57), (5, 73), (6, 89)],生成的代码为:

 if(i>=41)
  if(i>=73)
   if(i>=89)
    return 6;
   else
    return 5;
  else
   if(i>=57)
    return 4;
   else
    return 3;
 else
  if(i>=9)
   if(i>=25)
    return 2;
   else
    return 1;
  else
   return 0;

基准

基准测试基于我案例的127 * n / sqrt(n*n + 4194304) sigmoid函数,它们超出了输入范围[-8000000,8000000]。

Pentium M 1.2 GHz

测试查找 CPU时间:300000 测试数学 CPU时间:1460000

Raspberry Pi 2 800 MHz

测试查找 CPU时间:474094 测试数学 CPU时间:2897385

Raspberry Pi 3 1.2GHz

测试查找 CPU时间:369665 测试数学 CPU时间:1570066

Intel Core™2 Q6600 2.4 GHz

测试查找 CPU时间:73623 测试数学 CPU时间:797847