float_t有什么意义,应该何时使用?

时间:2011-03-22 10:46:47

标签: c gcc floating-point double

我正在与使用旧版本GCC(确切地说是3.2.3)的客户合作,但是想要升级,而且作为升级到新版本的绊脚石的一个原因是尺寸的差异类型为float_t,确实是正确的:

在GCC 3.2.3上

sizeof(float_t) = 12
sizeof(float) = 4
sizeof(double_t) = 12
sizeof(double) = 8

在GCC 4.1.2上

sizeof(float_t) = 4
sizeof(float) = 4
sizeof(double_t) = 8
sizeof(double) = 8

但这种差异的原因是什么?为什么尺寸变小,什么时候应该使用float_tdouble_t

3 个答案:

答案 0 :(得分:12)

float_t的原因是对于某些使用较大类型的处理器和编译器,例如float的long double可以更高效,因此float_t允许编译器使用更大的类型而不是float。

因此在使用float_t的OPs情况下,大小的变化是标准所允许的。如果原始代码想要使用较小的float大小,那么它应该使用float。

open-std doc

有一些基本原理
  

例如类型定义   float_t和double_t(定义于   < math.h>),旨在允许   有效利用架构   更高效,更广泛的格式。附件

答案 1 :(得分:9)

"为什么"是某些编译器会在浮点寄存器中返回浮点值。这些寄存器只有一种尺寸。例如,在X86上,它是80位宽。无论该类型是否已声明为float,double,float_t或double_t,返回浮点值的函数的结果都将放入此寄存器中。如果返回值的大小和浮点寄存器的大小不同,那么在某些时候需要一条指令向下舍入到所需的大小。

对于整数也需要相同类型的转换,但是对于后续的加法和减法,没有开销,因为有指令选择操作中涉及哪些字节。将整数转换为较小尺寸的规则指定最重要的位被抛弃,因此缩小尺寸的结果可以产生完全不同的结果(例如(短)(2147450880) - > -32768),但是由于某种原因,编程社区似乎没问题。

在进行浮点缩小时,结果被指定为四舍五入到最接近的可表示数字。如果整数遵循相同的规则,那么上面的例子将因此截断(短)(2147450880) - > +32767。显然,需要更多的逻辑来执行仅仅截断高位的操作。对于浮点数,指数和有效位数在float,double和long double之间改变大小,因此更复杂。此外,还存在无穷大,NaN,标准化数字和需要考虑的重整化数字之间的转换问题。硬件可以在与整数加法相同的时间内实现这些转换,但如果转换需要在软件中实现,则可能需要20条指令,这会对性能产生显着影响。由于C编程模型确保无论浮点是以硬件还是软件实现,都会生成相同的结果,因此软件必须执行这些额外的指令以符合计算模型。 float_t和double_t类型旨在公开最有效的返回值类型

编译器定义 FLT_EVAL_METHOD ,它指定在中间计算中使用多少精度。对于整数,规则是使用所涉及的操作数的最高精度来进行中间计算。这将对应于 FLT_EVAL_METHOD == 0。然而,原始K& R指定所有中间计算都以double形式完成,因此产生 FLT_EVAL_METHOD == 1。然而,随着IEEE浮点标准的引入,它在某些平台上变得司空见惯,特别是Macintosh PowerPC和Windows X86以长双80位执行中间计算,从而产生 FLT_EVAL_METHOD = = 2。

回归测试将受 FLT_EVAL_METHOD 计算模型的影响。因此,您的回归代码应考虑到这一点。一种方法是测试 FLT_EVAL_METHOD 并为每个模型分配不同的分支。类似的方法是测试sizeof(float_t),并具有不同的分支。第三种方法是使用某种epsilon来检查结果是否足够接近。

不幸的是,有一些计算基于计算结果做出决定,导致 true false ,使用epsilon无法解决。这发生在计算机图形中,例如,以确定点是在多边形内部还是外部,这确定是否应该填充特定像素。如果您的回归涉及其中之一,则不能使用epsilon方法,并且必须根据计算模型使用不同的分支。

解决模型之间的决策回归的另一种方法是将结果明确地转换为特定的期望精度。这大部分时间都适用于许多编译器,但有些编译器认为它们比你更聪明,并且拒绝进行转换。这发生在中间结果存储在寄存器中但在后续计算中使用的情况下。您可以在中间结果中尽可能多地抛弃精度,但编译器将不执行任何操作 - 除非您将中间结果声明为 volatile 。然后,这会强制编译器缩小尺寸并将中间结果存储在内存中指定大小的变量中,然后在需要进行计算时检索它。对于基本操作(+ - * /)和平方根,IEEE浮点标准 exact 。我相信sin(),cos(),exp(),log()等被指定为在最接近的数字表示结果的2 ULP(最低有效位置中的单位)内。长双(80位)格式旨在允许计算那些其他超越函数与最接近的数值可表示的结果。

这涵盖了该线程中提出(和隐含)的许多问题,但没有回答何时应该使用float_t和double_t类型的问题。显然,在与使用这些类型的API进行交互时,您需要这样做,尤其是在传递其中一种类型的地址时。

如果您首要关注的是性能问题,那么您可能需要考虑在计算和API中使用float_t和double_t类型。但最有可能的是,你获得的性能提升既不可测量也不明显。

但是,如果您担心不同编译器和不同机器之间的回归,您应该尽可能地避免使用这些类型,并且可以大量使用连接以确保跨平台兼容性。

答案 2 :(得分:6)

C99标准说:

  

类型   float_t double_t

     

是浮点类型,至少与float和double一样宽,并且double_t至少与float_t一样宽。如果FLT_EVAL_METHOD等于0,则float_tdouble_t分别为floatdouble;如果FLT_EVAL_METHOD等于1,则它们都是double;如果FLT_EVAL_METHOD等于2,则它们都是long double;对于FLT_EVAL_METHOD的其他值,它们在其他方面是实现定义的.178)

事实上,在以前的gcc版本中,默认情况下它们被定义为long double