sky*_*ing 69 c floating-point printf rationale c-standard-library
它看起来像它可能是,有(至少在C99),可以适用于长度修改int:%hhd,%hd,%ld和%lld指signed char,short,long和long long.甚至还有一个长度修饰符适用于double:%Lf均值long double.
问题是他们为什么省略float?按照模式,它可能是%hf.
Bas*_*tch 47
因为在C variadic函数调用中,任何float参数都被提升(即转换)为a double,所以printf获得a double并将用于va_arg(arglist, double)将其置于其实现中.
在过去(C89和K&R C),每个float参数都被转换为a double.当前标准省略了针对具有明确原型的固定arity函数的此促销.它与实现的ABI和调用约定有关(并且详细说明).实际上,float当作为参数传递时,值通常会被加载到双浮点寄存器中,但细节可能会有所不同.以Linux x86-64 ABI规范为例.
此外,没有实际的理由给出一个特定的格式控制字符串,float因为你可以根据需要调整输出的宽度(例如%8.5f),并且比在中%hd更有用(几乎必要)scanfprintf
除此之外,我想原因(省略%hf指定float- double在调用者中指定printf)是历史性的:首先,C是一种系统编程语言,而不是HPC(Fortran在HPC中可能直到20世纪90年代后期)并且float不是很重要; 它(并且仍然)被认为short是一种降低内存消耗的方法.今天的FPU足够快(在台式机或服务器计算机上)以避免使用,float除非作为使用更少内存的手段.你基本上应该相信每个float地方(可能在 FPU或CPU 内部)转换为double.
实际上,你的问题可能会被解释为:为什么%hd存在printf(它基本上没用,因为当你传递它时printf会得到一个;但是需要它!).我不知道为什么,但我想象比在系统编程中它可能更有用.intshortscanf
你可以花时间游说下一个ISO C标准得到%hf所接受printf的float(晋升为double在printf通话,就像short-s得到提升到int),与未定义行为时,双精度值超出界float-s和symetrically %hf所接受scanf的float指针.祝你好运.
Yu *_*Hao 15
因为默认参数促销.
printf()是一个变量参数函数(...在其签名中),所有float参数都被提升为double.
C11§6.5.2.2函数调用
6如果表示被调用函数的表达式具有不包含原型的类型,则对每个参数执行整数提升,并将具有类型的参数
float提升为double.这些被称为默认参数促销.7函数原型声明符中的省略号表示法导致参数类型转换在最后声明的参数之后停止.默认参数提升是在尾随参数上执行的.
aut*_*tic 10
由于调用可变参数函数时的默认参数提升,float值double在函数调用之前被隐式转换为,并且无法将float值传递给printf.由于无法传递float值printf,因此不需要值的显式格式说明符float.
话虽如此,AntoineL提出了在评论一个有趣的问题%lf(目前用于scanf对应的参数类型double *)可能曾经代表" long float",这是在预C89天这类代名词,按照第42页的C99理由.按照这种逻辑,可能有意义的%f是,它意味着代表float已转换为a的值double.
关于hh和h修饰符,%hhu并%hu为这些格式说明符提供明确定义的用例:您可以打印大型unsigned int或unsigned short不带强制转换的最低有效字节,例如:
printf("%hhu\n", UINT_MAX); // This will print (unsigned char) UINT_MAX
printf("%hu\n", UINT_MAX); // This will print (unsigned short) UINT_MAX
Run Code Online (Sandbox Code Playgroud)
没有特别明确地限定转换int为char或short将导致的转换,但它至少是实现定义的,这意味着需要实现来实际记录此决策.
应该遵循这种模式
%hf.
按照您观察到的模式,%hf应该将值转换float回范围之外float.然而,那种从收缩转换double到float 导致不确定的行为,以及有没有这样的事,作为一个unsigned float.你看到的模式没有意义.
要正式更正,%lf不表示long double参数,如果要传递long double参数,则会调用未定义的行为.从文档中明确指出:
l(ELL)...对在下一个无影响a,A,e,E,f,F,g,或G转换说明.
我很惊讶没有其他人接受过这个?%lf表示一个double参数,就像%f.如果要打印a long double,请使用%Lf(capital ell).
从此以后,由于前面提到的原因,因为默认参数促销,因此%lf对于两者而言,printf并且scanf对应于double和double *参数...... %f是特殊的.
......也%Ld不代表long.这意味着什么是未定义的行为.
从ISO C11标准,6.5.2.2 Function calls /6并/7讨论表达式上下文中的函数调用(我的重点):
6 /如果表示被调用函数的表达式具有不包含原型的类型,则对每个参数执行整数提升,并将具有float类型的参数提升为double.这些被称为默认参数促销.
7 /如果表示被调用函数的表达式具有包含原型的类型,则将参数隐式转换为相应参数的类型,就像通过赋值一样,将每个参数的类型作为不合格的版本它的声明类型.函数原型声明符中的省略号表示法导致参数类型转换在最后声明的参数之后停止.默认参数提升是在尾随参数上执行的.
这意味着原型中的任何float参数...都被转换为,double并且printf调用族以这种方式定义(7.21.6.11et seq):
int fprintf(FILE * restrict stream, const char * restrict format, ...);
Run Code Online (Sandbox Code Playgroud)
因此,由于printf()家庭调用无法实际接收浮点数,因此为它设置一个特殊的格式说明符(或修饰符)是没有意义的.
| 归档时间: |
|
| 查看次数: |
3392 次 |
| 最近记录: |