printf输出float和double类型数据的问题
以前都一直以为printf输出double类型使用的是%lf,今天才发现输出float和double使用的都是%f。这让我很是迷惑。因为printf是变参函数,参数的入栈和出栈都是由被调用者完成。printf识别各个参数完全是通过输出格式字符串中获取要输出的参数的类型。float和double分别占用4和8个字节的空间,当printf看到%f的时候如何知道这个数据是几个字节呢?难道float类型在入栈的时候也是自动按照double来如栈吗?
如果各位有知道其中原因的,还望不吝赐教。
[解决办法]
查MSDN得:
e double
E double
f double
g double
G double
所以,不是printf知道是什么类型,而是在输出时做了强制转换。
把float赋给double不会丢失精度一样。
[解决办法]
应该是将%f转换成double再入栈
[解决办法]
是因为使用了自动向上类型转换
[解决办法]
5楼说的基本没错,只不过不是类型提升,而是usual arithmetic conversions。
可变参数函数一般不进行类型检查,但printf的float是个例外,float在入栈前先被转换为double,这是为了兼容K&R C的做法。
[解决办法]
VC98\Include\STDARG.h对于系列变参宏的说明。
其际上,也确实如此,将float作为参数传递给变参函数,编译时,会自动将float的值转换为double,然后压入栈。
...
while ((ch = *format++) != _T('\0') && charsout >= 0) {
chclass = find_char_class(ch); /* find character class */
state = find_next_state(chclass, state); /* find next state */
/* execute code for each state */
switch (state) {
...
case ST_SIZE:
/* just read a size specifier, set the flags based on it */
switch (ch) {
#if !LONG_IS_INT
[解决办法]
!defined (_UNICODE)
case _T('l'):
flags
[解决办法]
= FL_LONG; /* 'l' => long int or wchar_t */
break;
#endif /* !LONG_IS_INT
[解决办法]
!defined (_UNICODE) */
...
case ST_TYPE:
switch (ch) {
...
case _T('E'):
case _T('G'):
capexp = 1; /* capitalize exponent */
ch += _T('a') - _T('A'); /* convert format char to lower */
/* DROP THROUGH */
case _T('e'):
case _T('f'):
case _T('g'): {
/* floating point conversion -- we call cfltcvt routines */
/* to do the work for us. */
flags
[解决办法]
= FL_SIGNED; /* floating point is signed conversion */
#if !LONGDOUBLE_IS_DOUBLE
/* do the conversion */
if (flags & FL_LONGDOUBLE) {
LONGDOUBLE tmp;
tmp=va_arg(argptr, LONGDOUBLE);
/* Note: assumes ch is in ASCII range */
_cldcvt(&tmp, text.sz, (char)ch, precision, capexp);
} else
#endif /* !LONGDOUBLE_IS_DOUBLE */
{
DOUBLE tmp;
tmp=va_arg(argptr, DOUBLE);
/* Note: assumes ch is in ASCII range */
_cfltcvt(&tmp,text.sz, (char)ch, precision, capexp);
}
...
}
break;
以上是printf实现的主要代码,其中须要说明的几个宏:
1.LONG_IS_INT:printf实现代码中始终定义为1;
2.LONGDOUBLE_IS_DOUBLE:在定义了_WIN32宏的情况下,被定义为1;在定义了_M_MPPC情况下,被定义为1;在定义了_M_M68K情况下,被定义为0;_WIN32、_M_MPPC与_M_M68K为Microsoft C++预先定义的,编译WIN32程序中是定义的_WIN32,因此LONGDOUBLE_IS_DOUBLE被定义为1;
3.DOUBLE:被定义为double。
这样,上面的代码就好看了,主要的是:
{
DOUBLE tmp;
tmp=va_arg(argptr, DOUBLE);
/* Note: assumes ch is in ASCII range */
_cfltcvt(&tmp,text.sz, (char)ch, precision, capexp);
}
因此"%f"也是按double读取的8字节,若实参为float,编译时会自动转换为double的。
[解决办法]
float隐式转换成double类型之后,再入栈。
[解决办法]
float是先转的double然后入的栈,很多贴说过这个的。。。