我想在这些函数中替换浮点数学而不会损失太多精度,因为我没有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) );
}
答案 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
替换现有代码中的double
或fixed
来使用它。 。它使用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]。
测试查找 CPU时间:300000 测试数学 CPU时间:1460000
测试查找 CPU时间:474094 测试数学 CPU时间:2897385
测试查找 CPU时间:369665 测试数学 CPU时间:1570066
测试查找 CPU时间:73623 测试数学 CPU时间:797847