C中浮点类型的整数值
#include<stdio.h>
int main()
{
float a;
printf("Enter a number:");
scanf("%f",&a);
printf("%d",a);
return 0;
}
我在Ubuntu中运行程序gcc
。 对于数值 -C中浮点类型的整数值
3.3 it gives value 1610612736
3.4 it gives value 1073741824
3.5 it gives value 0
3.6 it gives value -1073741824
4 it gives value 0
5 it gives value 0
发生了什么事?为什么打印这些值?我故意这样做,但想知道为什么会发生这种情况。细节非常感谢!
printf
函数不知道您传入的格式类型,因为该部分是可变参数。
int printf(const char* format, ...);
// ^^^
在C标准,传递一个float
会自动提升为double
(C11§6.5.2.2/ 6),而不是其他将在发送方完成。
在printf
里面,因为它不知道...
thingie(§6.7.6.3/ 9)的类型,所以它必须使用其他地方的提示 - 格式字符串。既然你已经通过了"%d"
,它正在告诉函数,预计有一个int
。
根据C标准,这导致了未定义的行为(§7.21.6.1/ 8-9),其中包括打印一些奇怪的数字,故事结束的可能性。
但是究竟发生了什么?在大多数平台中,double
被表示为“IEEE 754 binary64”格式,并且binary32格式表示float
。您所输入的号码将被转换为浮动,只有具有意义的23位,这意味着数字将这样的近似:
3.3 ~ (0b1.10100110011001100110011) × 2¹ (actually: 3.2999999523162842...)
3.4 ~ (0b1.10110011001100110011010) × 2¹ (actually: 3.4000000953674316...)
3.5 = (0b1.11 ) × 2¹ (actually: 3.5)
3.6 ~ (0b1.11001100110011001100110) × 2¹ (actually: 3.5999999046325684...)
4 = (0b1 ) × 2² (actually: 4)
5 = (0b1.01 ) × 2² (actually: 5)
现在我们将它转换为加倍,它具有意义的53位,我们必须在这些数字的末尾插入30个二进制“0”,以产生例如
3.299999952316284 = 0b1.10100110011001100110011000000000000000000000000000000 ×2¹
这些主要是推导这些数字,它们是实际表示:
3.3 → 400A6666 60000000
3.4 → 400B3333 40000000
3.5 → 400C0000 00000000
3.6 → 400CCCCC C0000000
4 → 40100000 00000000
5 → 40140000 00000000
我建议使用http://www.binaryconvert.com/convert_double.html看不出这分解到±米×2 ë格式。
不管怎样,我想你的系统是在正常设置在x86/x86_64的/ ARM,这意味着数字使用little-endian format在内存中摆出来,所以传递的参数会像
byte
#0 #1 ... #4 ... #8 ....
+----+----+----+----+ +----+----+----+----+----+----+----+----+
| 08 | 10 | 02 | 00 | | 00 | 00 | 00 | 60 | 66 | 66 | 0A | 40 | ....
+----+----+----+----+ +----+----+----+----+----+----+----+----+
address of "%d" content of 3.299999952316284
(just an example)
里面的printf
,它消耗格式字符串"%d"
,解析它,然后找出该一个int
需要,因为%d,所以4个字节从可变参数输入拍摄,这是:
byte
#0 #1 ... #4 ... #8 ....
+ - -+ - -+ - -+ - -+ +====+====+====+====+ - -+ - -+ - -+ - -+
: 08 : 10 : 02 : 00 : | 00 | 00 | 00 | 60 | 66 : 66 : 0A : 40 : ....
+ - -+ - -+ - -+ - -+ +====+====+====+====+ - -+ - -+ - -+ - -+
address of "%d" ~~~~~~~~~~~~~~~~~~~
this, as an 'int'
所以,printf的将收到0x60000000,并将其显示为十六进制整数,即1610612736,这就是您看到该结果的原因。其他数字可以类似解释。
3.3 → ... 60000000 = 1610612736
3.4 → ... 40000000 = 1073741824
3.5 → ... 00000000 = 0
3.6 → ... C0000000 = -1073741824 (note 2's complement)
4 → ... 00000000 = 0
5 → ... 00000000 = 0
我假设现在发布的其他答案缺少重点:我认为你故意使用不同的转换进行扫描和打印,并且想要了解结果。如果你确实犯了一个错误,那么你可以忽略我的答案。
基本上,你需要阅读this article,这将解释如何定义浮点数的位模式,然后写出每个数字的位模式。鉴于你了解整数如何存储,你应该得到你的答案。
d
您在第二个printf
语句中使用的转换说明符需要参数类型为int
。在C默认参数提升后,您的参数a
的类型为double
。传递一个不同类型的参数是一个未定义的行为,并且像往常一样未定义的行为,任何事情都可能发生。
如果您想知道到底发生了什么,请尝试printf('0x%08x\n', a);
而不是printf("%d",a);
。你将能够看到变量a的实际位,而不是printf("%d",a);
给你的东西。
只是因为printf ("%d",a);
:认为a中的内存是一个int,因此它将其内容解释为int。 和printf("%f",a);
考虑一个内存的内存为一个浮动它是真的...
但如果你写printf("%d",(int)a)
; // a被转换为int(通过(int)强制转换)。所以打印出a
的近似值。
在C中,浮点数作为参数传递给具有可变数量的参数的函数被提升为双精度。这就是为什么在printf函数的格式字符串的引用中,您不会看到浮点数和双精度的不同格式说明符。所以,当传递给printf时,你的“a”从32位浮点数转换为64位双精度浮点数。恰巧4和5被表示为双精度,64位中有32位是零,而这些零位是printf函数解释为整数的那些位,因为您告诉它打印一个整数。
printf()
使用第一个参数中提到的格式说明符来解释其可变长度参数。 printf()
的签名如下。
int printf(const char *format, ...);
所以printf()
代码可能会被写成这样使用stdarg.h
。
int printf(const char *format, ...) {
va_list ap;
char *p, *sval;
int ival;
float fval;
va_start(ap, format);
for(p=format; *p ; p++) {
if (*p != '%') {
putchar(*p);
continue;
}
switch(*++p) {
case 'd':
ival = va_arg(ap, int);
break;
case 'f':
fval = va_arg(ap, float);
break;
case 's':
for (sval = va_arg(ap, char *); *sval; sval++);
break;
default:
putchar(*p);
break;
}
}
va_end(ap);
}
所以,如果你通过%d
的float
那么你可以找出printf()
里面会发生什么。printf()
将把float
变量解释为int
,并且此行为未定义!
希望这会有所帮助!
+1需要时间写出所有这些。 :) – Mysticial 2011-12-29 19:29:27
为了完整起见,有两个有用的补码链接,[Two's Complement - Wikipedia](http://en.wikipedia.org/wiki/Two%27s_complement)和[Two's Complement notes Thomas Finley](http:// tfinley。净/笔记/ cps104/twoscomp.html)。 – mctylr 2011-12-29 20:04:43