覆盖GCC的varargs参数提升

时间:2019-10-01 05:48:31

标签: c gcc variadic-functions

我正在为运行Cortex-M4架构(具有单精度浮点数而不是双精度浮点数)的ARM处理器编写一段代码。我遇到的问题是在使用varargs时,编译器尝试将我的float提升为double。有什么办法可以关闭此功能吗?还是指定其他促销策略?我查看了GCC手册,但找不到任何东西。一个简单的例子就是尝试编写自己的打印文件...

void myprintf(const char *fmt, ...) {
  // .. parse fmt and come across %f

  double dv = va_arg(args, double); // Compiles but doesn't link

  float fv = va_arg(args, float); // Doesn't compile (warns about promotion)
}

更新:根据要求,我要使用的调用命令为myprintf("%f", 1.0f);。问题在于,Cortex-M4不支持双精度,因此当1.0f提升为双精度时……不可以。因此,使用va_arg(args, double)读回它可以编译,但不会链接,因为与double有关的各种__aeabi_函数不存在。我想做的是禁用GCC将浮点数提升为两倍(如果可能)。

更新:我有一个当前的解决方法,基本上只接受指向浮点数的指针而不是浮点数。我仍然想禁用促销,因为这不是一个很好的解决方案。

1 个答案:

答案 0 :(得分:4)

将vararg float参数转换为double不是gcc怪癖或附加组件。 C标准要求它。 §6.5.2.3(函数调用):

  
      
  1. 如果表示被调用函数的表达式的类型不包括原型,则对每个自变量执行整数提升,并将类型为float的自变量提升为double。这些称为默认参数提升。 …
  2.   
  3. 如果表示被调用函数的表达式的类型确实包含原型,则将参数隐式转换为相应参数的类型,就像通过赋值一样,将每个参数的类型视为非限定版本其声明的类型。函数原型声明器中的省略号引起参数类型转换在最后声明的参数之后停止。 默认参数提升是对尾随参数进行的。
  4.   

Gcc确实提供了一些与C标准不兼容的选项,但是绝大多数仅在标准不支持的情况下才增加行为。如此大的变化将影响标准调用约定,从而使编译后的代码与其他编译后的代码(例如库,标准或其他)不兼容。 Gcc没有提供这样的选项。其他编译器也许可以,但是我不知道哪个可以。

请注意,如果double是32位值,则不可能在标准的约束下仅使floatfloat的同义词。 §5.2.4.2.2中的最低精度要求实际上要求双精度除指数和符号外还至少具有32位尾数。这比64位IEEE-749双精度所提供的精度要低得多,但显然比32位值所能提供的精度更高。

对于编译器,当然可以提供一个选项,其中double以不太精确且计算速度更快的格式表示,例如两个float的尾数不重叠。 (也就是说,指数之间至少相差了尾数宽度。)至少一种传统实现IIRC使用了这种表示法或类似表示法,正是因为它允许单精度硬件用于双精度计算。 。这也使将float转换为double变得微不足道;仅需将低阶值设为0.0。另外,编译器可以使用结合了short指数的32位尾数,从而允许使用整数ALU。

不过,我也不认为GCC可以提供这种选择。