C语言标准中只提供了float型和double 型两种浮点类型。但是有些情况下,我们需要比double 型更大数据范围或更多的有效位数,为此,GCC 为我们提供了__float128型浮点类型。这种扩展类型可以表示的数据范围非常大,大约为-10e4932到10e4932,可以表示的最接近0的数大约为3.36*e-4932,可以表示的10进制有效位大约为33位。比起double 类型,无论是数据范围还是精度都有了一个极大的提升。当然,由于现在日常使用的计算机的CPU的浮点处理器不能直接对__float128进行运算,所以即使是两个简单的__float128型变量相加运算都要耗费CPU的相当多条指令来完成。因此程序中使用__float128 型后运算速度会降低很多。
除了对__float128数据类型的支持之外,GCC还提供一个GCC Quad-PrecisionMath Library(简称为quadmath),里面提供了基本数学函数库中相应函数的__float128型的版本。
下面简单的讲讲如何在程序中使用__float128数据和quadmath库。
首先,__float128 型和各种相关的函数声明在了 quadmath.h 中。因此,如果要在C程序中使用__float128 型浮点数就要包含这个头文件。相应的处理函数打包在了 libquadmath.o 中,为此,在最后链接(link)阶段要包括这个库文件,也就是加入链接命令行参数 -lquadmath。
除了 __float128 型,还有 __complex128 型,它是complex 型向4精度浮点型的升级。
既然有了__float128类型,就要提供对这种类型的输入输出的支持。GCC扩展了C语言的标准,增加了以Q结尾的__float128类型的数值常量。比如下面的例子:
__float128 a = 1.23456789012345Q;
还提供了两个函数以支持__float128类型与字符串间的转换:
__float128 strtoflt128 (const char *s, char **sp) int quadmath_snprintf (char *str, size_t size, const char *format, ...)
strtoflt128 函数是 strtod 函数的__float128类型版本,参数类型与strtod 函数完全一样,只不过返回值是__float128类型。
quadmath_snprintf 函数是snprintf的__float128类型版本。通过在传统的解析字符串中增加了字符Q来表示__float128类型,比如“%f”表示的读取一个浮点数,“%Qf”则是读取一个__float128类型的数据。除了“%Qf”之外,还可以用“%QF”、“%Qe”、“%QE”、“%Qg”、“%QG”。
Quadmath 库中包含的数学函数如下,基本上就是对应的double 类型的函数的函数名后加了后缀q。因此,每个函数的含义就不一一解释了。
__float128 acosq (__float128) __float128 acoshq (__float128) __float128 asinq (__float128) __float128 asinhq (__float128) __float128 atanq (__float128) __float128 atanhq (__float128) __float128 atan2q (__float128, __float128) __float128 cbrtq (__float128) __float128 ceilq (__float128) __float128 copysignq (__float128, __float128) __float128 coshq (__float128) __float128 cosq (__float128) __float128 erfq (__float128) __float128 erfcq (__float128) __float128 expq (__float128) __float128 expm1q (__float128) __float128 fabsq (__float128) __float128 fdimq (__float128, __float128) int finiteq (__float128) __float128 floorq (__float128) __float128 fmaq (__float128, __float128, __float128) __float128 fmaxq (__float128, __float128) __float128 fminq (__float128, __float128) __float128 fmodq (__float128, __float128) __float128 frexpq (__float128, int *) __float128 hypotq (__float128, __float128) int isinfq (__float128) int ilogbq (__float128) int isnanq (__float128) __float128 j0q (__float128) __float128 j1q (__float128) __float128 jnq (int, __float128) __float128 ldexpq (__float128, int) __float128 lgammaq (__float128) long long int llrintq (__float128) long long int llroundq (__float128) __float128 logq (__float128) __float128 log10q (__float128) __float128 log2q (__float128) __float128 log1pq (__float128) long int lrintq (__float128) long int lroundq (__float128) __float128 modfq (__float128, __float128 *) __float128 nanq (const char *) __float128 nearbyintq (__float128) __float128 nextafterq (__float128, __float128) __float128 powq (__float128, __float128) __float128 remainderq (__float128, __float128) __float128 remquoq (__float128, __float128, int *) __float128 rintq (__float128) __float128 roundq (__float128) __float128 scalblnq (__float128, long int) __float128 scalbnq (__float128, int) int signbitq (__float128) void sincosq (__float128, __float128 *, __float128 *) __float128 sinhq (__float128) __float128 sinq (__float128) __float128 sqrtq (__float128) __float128 tanq (__float128) __float128 tanhq (__float128) __float128 tgammaq (__float128) __float128 truncq (__float128) __float128 y0q (__float128) __float128 y1q (__float128) __float128 ynq (int, __float128) /* Prototypes for complex functions */ __float128 cabsq (__complex128) __float128 cargq (__complex128) __float128 cimagq (__complex128) __float128 crealq (__complex128) __complex128 cacosq (__complex128) __complex128 cacoshq (__complex128) __complex128 casinq (__complex128) __complex128 casinhq (__complex128) __complex128 catanq (__complex128) __complex128 catanhq (__complex128) __complex128 ccosq (__complex128) __complex128 ccoshq (__complex128) __complex128 cexpq (__complex128) __complex128 cexpiq (__float128) __complex128 clogq (__complex128) __complex128 clog10q (__complex128) __complex128 conjq (__complex128) __complex128 cpowq (__complex128, __complex128) __complex128 cprojq (__complex128) __complex128 csinq (__complex128) __complex128 csinhq (__complex128) __complex128 csqrtq (__complex128) __complex128 ctanq (__complex128) __complex128 ctanhq (__complex128)下面举一个简单的例子:
#include <stdio.h> #include <stdlib.h> #include <quadmath.h> int main(void) { __float128 r1; __float128 r2; __float128 r3; char buf[64]; char *pEnd = buf; r1 = strtoflt128 ("1.234567890123456789 9.876543210987654321e5", &pEnd); r2 = strtoflt128 (pEnd, NULL); quadmath_snprintf (buf, sizeof buf, "%+-#*.20Qf", 64, r1); puts(buf); quadmath_snprintf (buf, sizeof buf, "%+-#*.20Qe", 64, r2); puts(buf); r3 = hypotq (r1, r2); quadmath_snprintf (buf, sizeof buf, "%.30Qe", 64, r3); puts(buf); return 0; }
输出的结果为:
+1.23456789012345678900
+9.87654321098765432100e+05
9.876543210995370370242956091483e+05
结果是否准确,我们可以用 maxima 来验算一下:
fpprec:35$
r1:bfloat(1.234567890123456789b0)$
r2:bfloat(9.876543210987654321b05)$
r3:sqrt(r1*r1+r2*r2);
(%o4) 9.8765432109953703702429560914832608b5
小数点后30位全都是正确的。说明quadmath 库的计算精度还是相当不错的,至少hypotq函数的精度相当高。
__float128型浮点不是C语言标准类型,因此这里的介绍只对GCC编译器成立。另外,我使用的gcc的版本信息如下:
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=c:/mingw/bin/../libexec/gcc/mingw32/4.6.1/lto-wrapper.exe
mingw32
../gcc-4.6.1/configure--enable-languages=c,c++,fortran,objc,obj-c++ --disable-sjlj-exceptions--with-dwarf2 --enable-shared --enable-libgomp --disable-win32-registry--enable-libstdcxx-debug --enable-version-specific-runtime-libs --build=mingw32--prefix=/mingw
win32
gcc 4.6.1 (GCC)
其他版本的GCC 运行的结果不一定与这里相同。
如果需要更高的计算精度,就要考虑一些专用的多精度数值计算库了,比如大名鼎鼎的GMP。