#include <stdio.h>
int main() {
float a = 1234.5f;
printf("%d\n", a);
return 0;
}
Wyświetla 0
!! Jak to możliwe? Jaki jest tego powód?
Celowo umieściłem %d
w printf
oświadczeniu znak, aby zbadać zachowanie printf
.
Odpowiedzi:
To dlatego, że %d
oczekuje, int
ale dostarczyłeś pływaka.
Użyj %e
/ %f
/, %g
aby wydrukować pływak.
O tym, dlaczego wypisywane jest 0: Liczba zmiennoprzecinkowa jest konwertowana na double
przed wysłaniem do printf
. Liczba 1234,5 w podwójnej reprezentacji w little endian to
00 00 00 00 00 4A 93 40
A %d
zużywa 32-bitową liczbę całkowitą, więc drukowane jest zero. (W ramach testu możesz printf("%d, %d\n", 1234.5f);
uzyskać wynik 0, 1083394560
.)
Jeśli chodzi o powód, dla którego float
jest konwertowany na double
, jak prototyp printf int printf(const char*, ...)
, z 6.5.2.2/7,
Notacja wielokropka w deklaratorze prototypu funkcji powoduje zatrzymanie konwersji typu argumentu po ostatnim zadeklarowanym parametrze. Domyślne promocje argumentów są wykonywane na argumentach końcowych.
i od 6.5.2.2/6,
Jeśli wyrażenie oznaczające wywoływaną funkcję ma typ, który nie zawiera prototypu, promocje liczb całkowitych są wykonywane dla każdego argumentu, a argumenty, które mają typ,
float
są promowane dodouble
. Nazywa się to domyślnymi promocjami argumentów .
(Dzięki Alok, że się o tym dowiedziałeś.)
printf
jest to funkcja wariadyczna, a standard mówi, że w przypadku funkcji wariadycznych przed przekazaniem a float
jest konwertowany na double
.
printf()
wywołuje niezdefiniowane zachowanie .
Z technicznego punktu widzenia nie ma to , każda biblioteka realizuje swoje własne, a zatem metoda próbuje badania jest zachowanie robiąc to, co robisz, nie będzie znacznie użytkowania. Możesz próbować przestudiować zachowanie w swoim systemie, a jeśli tak, powinieneś przeczytać dokumentację i spojrzeć na kod źródłowy printf
printf
printf
printf
czy jest dostępny dla Twojej biblioteki.
Na przykład na moim Macbooku otrzymuję dane wyjściowe 1606416304
z twoim programem.
Powiedziawszy to, kiedy przekazujesz a float
do funkcji wariadycznej, float
jest ona przekazywana jako a double
. Więc twój program jest równoważny zadeklarowaniu a
jako double
.
Aby zbadać bajty a double
, możesz zobaczyć tę odpowiedź na ostatnie pytanie tutaj na SO.
Zróbmy to:
#include <stdio.h>
int main(void)
{
double a = 1234.5f;
unsigned char *p = (unsigned char *)&a;
size_t i;
printf("size of double: %zu, int: %zu\n", sizeof(double), sizeof(int));
for (i=0; i < sizeof a; ++i)
printf("%02x ", p[i]);
putchar('\n');
return 0;
}
Po uruchomieniu powyższego programu otrzymuję:
size of double: 8, int: 4
00 00 00 00 00 4a 93 40
Tak więc pierwsze cztery bajty double
okazały się równe 0, co może być powodem, dla którego otrzymałeś 0
wynik swojego printf
wywołania.
Aby uzyskać ciekawsze wyniki, możemy nieco zmienić program:
#include <stdio.h>
int main(void)
{
double a = 1234.5f;
int b = 42;
printf("%d %d\n", a, b);
return 0;
}
Po uruchomieniu powyższego programu na moim Macbooku otrzymuję:
42 1606416384
Z tym samym programem na komputerze z systemem Linux otrzymuję:
0 1083394560
int
argumenty są przekazywane w innych rejestrach niż double
argumenty. printf
with %d
przyjmuje int
argument, który %d
wynosi 42, a drugi prawdopodobnie wypisuje śmieci, ponieważ nie było drugiego int
argumentu.
Specyfikator %d
mówi, printf
że ma oczekiwać liczby całkowitej. Zatem pierwsze cztery (lub dwa, w zależności od platformy) bajty zmiennej zmiennoprzecinkowej są interpretowane jako liczba całkowita. Jeśli zdarzy się, że są równe zero, drukowane jest zero
Binarna reprezentacja 1234,5 jest podobna do
1.00110100101 * 2^10 (exponent is decimal ...)
W przypadku kompilatora C, który reprezentuje w float
rzeczywistości podwójne wartości IEEE754, bajty byłyby (gdybym się nie pomylił)
01000000 10010011 01001010 00000000 00000000 00000000 00000000 00000000
W systemie Intel (x86) z małą endianess (tj. Najmniej znaczący bajt pojawia się jako pierwszy), ta sekwencja bajtów jest odwracana, tak że pierwsze cztery bajty są zerowe. To znaczy, co się printf
drukuje ...
Zobacz ten artykuł w Wikipedii dla reprezentacji zmiennoprzecinkowej zgodnie z IEEE754.
Dzieje się tak z powodu reprezentacji liczby zmiennoprzecinkowej w systemie binarnym. Konwersja na liczbę całkowitą pozostawia 0.
Ponieważ wywołałeś niezdefiniowane zachowanie: naruszyłeś kontrakt metody printf (), okłamując ją co do typów parametrów, więc kompilator może robić, co mu się podoba. Może to spowodować, że na wyjściu programu pojawi się komunikat „dksjalk is a ninnyhead !!!” i technicznie nadal byłoby właściwe.
Powodem jest to, że printf()
jest to dość głupia funkcja. W ogóle nie sprawdza typów. Jeśli powiesz, że pierwszym argumentem jest an int
(i to właśnie z nim mówisz %d
), to on ci wierzy i zajmuje tylko bajty potrzebne do int
. W tym przypadku, zakładając, że twój komputer używa czterobajtów int
i ośmiu bajtów double
( float
jest konwertowany na double
wewnętrzny printf()
), pierwsze cztery bajty a
będą po prostu zerami, a to zostanie wydrukowane.
%d
jest dziesiętna
%f
jest pływający
zobacz więcej tutaj .
Otrzymujesz 0, ponieważ liczby zmiennoprzecinkowe i liczby całkowite są reprezentowane inaczej.
Wystarczy użyć odpowiedniego specyfikatora formatu (% d,% f,% s, itd.) Z odpowiednim typem danych (int, float, string itp.).
To nie jest liczba całkowita. Spróbuj użyć %f
.