La función printf
no conoce el tipo de formato que pasó, porque esa parte es variadica.
int printf(const char* format, ...);
// ^^^
En el estándar C, pasando por una float
serán promovidos automáticamente a un double
(C11§6.5.2.2/6), y nada más se llevará a cabo en el lado del llamante.
Dentro de printf
, ya que no conoce el tipo de ese ...
thingie (§6.7.6.3/9), tiene que usar la sugerencia de otro lugar - la cadena de formato. Desde que pasó el "%d"
, le dice a la función que se espera un int
.
De acuerdo con el estándar C, esto conduce a un comportamiento indefinido (§7.21.6.1/8-9), que incluye la posibilidad de imprimir un número extraño, al final de la historia.
Pero, ¿qué está pasando realmente? En la mayoría de las plataformas, un double
se representa como formato "IEEE 754 binary64" y un float
en formato binary32. Los números que ha introducido se convierten en un flotador, que sólo tiene 23 bits de importancia, lo que significa que los números se aproximan así:
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)
Ahora nos convertimos esta duplicar, que tiene 53 bits de significación , que tenemos que insertar 30 "0" binarios al final de estos números, para producir, por ejemplo,
3.299999952316284 = 0b1.10100110011001100110011000000000000000000000000000000 ×2¹
Estos son principalmente para derivar la representación real de esos números, que son:
3.3 → 400A6666 60000000
3.4 → 400B3333 40000000
3.5 → 400C0000 00000000
3.6 → 400CCCCC C0000000
4 → 40100000 00000000
5 → 40140000 00000000
recomiendo el uso de http://www.binaryconvert.com/convert_double.html para ver cómo esto se descompone al ± m × 2 correo formato.
De todos modos, supongo que su sistema es un sistema x86/x86_64/ARM en un entorno normal, lo que significa que los números se establecen en la memoria utilizando little-endian format, por lo que los argumentos que se pasan a ser como
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)
Dentro de la printf
, que consume la cadena de formato "%d"
, lo procesa, y luego se entera de que un int
es necesaria debido a% d, por lo que 4 bytes se toman de la entrada variadic, que es:
byte
#0 #1 ... #4 ... #8 ....
+ - -+ - -+ - -+ - -+ +====+====+====+====+ - -+ - -+ - -+ - -+
: 08 : 10 : 02 : 00 : | 00 | 00 | 00 | 60 | 66 : 66 : 0A : 40 : ....
+ - -+ - -+ - -+ - -+ +====+====+====+====+ - -+ - -+ - -+ - -+
address of "%d" ~~~~~~~~~~~~~~~~~~~
this, as an 'int'
así, printf recibirá 0x60000000 y lo mostrará como un número entero decimal, que es 1610612736, por lo que puede ver ese resultado. Los otros números se pueden explicar de manera similar.
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
+1 por tomarse el tiempo para escribir todo esto. :) – Mysticial
Para completar, un par de enlaces de complementos de Dos útiles, [Complemento de dos - Wikipedia] (http://en.wikipedia.org/wiki/Two%27s_complement) y [Dos notas de complemento Thomas Finley] (http: // tfinley.net/notes/cps104/twoscomp.html). – mctylr