我正在编写一些数值模拟代码,因此我非常清楚我使用的浮点类型,并且经常在
float
、double
和 long double
之间切换。我发现 tgmath.h 应该通过提供有用的宏来解决这个问题,但我无法让它工作!在以下代码中, sqrt(10.0f)
返回完整的双精度平方根,而 sqrtf
的行为符合预期。
#include <tgmath.h>
#include <iostream>
#include <iomanip>
int main(){
std::cout<<std::setprecision(16);
std::cout<<sqrt(10.0f)<<std::endl;
std::cout<<sqrtf(10.0f)<<std::endl;
return 0;
}
我希望这会输出相同的数字两次,因为
sqrt(float)
应该通过 tgmath 的功能计算为 sqrtf(float)
。相反,它输出双浮点精度值,然后输出单精度值。
3.16227766016838
3.162277698516846
我使用的是 Ubuntu,我使用命令
g++ floattest.cpp -std=c++11 -o floattest
进行编译。较高版本的 C++ 行为相同,较低版本 -std=c++03
和 -std=c++98
都无法使用 error: ‘__builtin_tgmath’ was not declared in this scope
进行编译。编译错误确实显示 In file included from /usr/include/c++/11/tgmath.h
。
是的,我完全忽略了 tgmath.h 的标头吗?我将如何运行依赖于 tgmath.h 的旧 C++ 代码?对于我当前的项目,我可以定义自己的 C++11 _Generics,但很高兴知道发生了什么。
Ubuntu版本:22.04 g++版本:g++(Ubuntu 11.4.0-1ubuntu1~22.04)11.4.0
<tgmath.h>
定义为 (by [tgmath.h.syn]):
#include <cmath> #include <complex>
...这并不能保证定义了名为
::sqrt
的函数。
在 glibc 中,这些签名是暴露的:
// (In the global :: namespace, defined by including <math.h>)
float sqrtf(float);
double sqrt(double);
long double sqrtl(long double);
// (Added for C++ <cmath>)
namespace std {
float sqrtf(float);
long double sqrtl(long double);
float sqrt(float);
double sqrt(double);
long double sqrt(long double);
}
因此
sqrt(10.0f)
只能找到 ::sqrt(double)
,将其转换为双精度。
要在选择正确函数的宏中获得
_Generic
的 C 行为,请使用重载的 std::sqrt
。
对旧的(且不正确的)C++ 代码的快速修复是全局命名空间中的
using std::sqrt;
,但从长远来看,最好只移植 sqrt
->std::sqrt