在我们看到的math-headers中
extern float fabsf(float);
extern double fabs(double);
extern long double fabsl(long double);
...
extern float fmodf(float, float);
extern double fmod(double, double);
extern long double fmodl(long double, long double);
Run Code Online (Sandbox Code Playgroud)
为什么每种类型都有一个功能?这不是很多重复的代码吗?如果我在哪里写一个lerp函数或一个钳位函数,我需要为每种类型写一个吗?
似乎我们将有重复的代码,其中只有一件事发生了变化 - 类型.
extern float clampf(float value, float min, float max)
{
if(value > max)
return max;
if(value < min)
return min;
return value;
}
extern double clamp(double value, double min, double max)
{
if(value > max)
return max;
if(value < min)
return min;
return value;
}
Run Code Online (Sandbox Code Playgroud)
问题1:这种结构的历史原因是什么?
问题2:我应该遵循相同的模式吗?或者我应该只实现double-kind,因为它是最常见的?
问题3:或者我应该使用macro's来完全克服类型问题?
从历史上看(大约C89及之前),数学库只包含这些函数的双精度版本,这就是为什么这些版本没有后缀的原因.如果你需要计算a的正弦float,你要么编写自己的实现,要么(更有可能!)你只需写:
float x;
float y = sin(x);
Run Code Online (Sandbox Code Playgroud)
但是,这会在现代架构上引入一些开销.具体来说,在当今最常见的体系结构中,编译器必须发出如下所示的代码:
convert x to double
call sin
convert result to float
Run Code Online (Sandbox Code Playgroud)
这些转换非常快(通常与添加相同),但它们仍然有一些成本.除了转换成本之外,sin需要提供具有~53位精度的结果,如果结果将被转换回单精度,则其中一半以上被完全浪费.在这两个因素之间,专用的单精度sin程序可能快两倍; 这是一些非常常用的库函数的重大胜利!
如果我们查看类似的函数fabs(并假设编译器不是简单地内联并降低它们),情况会更糟糕. fabs在典型的现代架构中,是一种简单的按位和操作.因此,将呼叫包围的两个转换(如果你拥有的话double)比操作本身要贵得多,并且很容易造成5倍的减速.这就是为什么添加这些函数的多个版本以支持每种FP类型的原因.
如果你不想跟踪所有这些,你可以#include <tgmath.h>根据参数的类型推断出正确使用的函数(意思是
sin((float)x)
Run Code Online (Sandbox Code Playgroud)
会产生一个调用sinf(x),而
sin((long double)x)
Run Code Online (Sandbox Code Playgroud)
会打电话sinl(x)).
在你自己的代码,你通常知道的先验什么你的参数类型是,只需要支持一个或者两个类型. clamp和lerp特别是图形操作,并且几乎普遍仅用于单精度变体.
顺便说一句,事实上,你正在使用clamp,并lerp是,你可能想看看写在OpenCL的,而不是C /的OBJ-C代码中的非常好的迹象; OpenCL数学库为您实现这些操作(以及许多其他类似操作),并提供适用于各种基本类型(包括向量)的实现.
| 归档时间: |
|
| 查看次数: |
253 次 |
| 最近记录: |