#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 %dw printfoświadczeniu znak, aby zbadać zachowanie printf.
Odpowiedzi:
To dlatego, że %doczekuje, intale dostarczyłeś pływaka.
Użyj %e/ %f/, %gaby wydrukować pływak.
O tym, dlaczego wypisywane jest 0: Liczba zmiennoprzecinkowa jest konwertowana na doubleprzed wysłaniem do printf. Liczba 1234,5 w podwójnej reprezentacji w little endian to
00 00 00 00 00 4A 93 40
A %dzuż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 floatjest 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,
floatsą promowane dodouble. Nazywa się to domyślnymi promocjami argumentów .
(Dzięki Alok, że się o tym dowiedziałeś.)
printfjest to funkcja wariadyczna, a standard mówi, że w przypadku funkcji wariadycznych przed przekazaniem a floatjest 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 printfprintfprintfprintf czy jest dostępny dla Twojej biblioteki.
Na przykład na moim Macbooku otrzymuję dane wyjściowe 1606416304z twoim programem.
Powiedziawszy to, kiedy przekazujesz a floatdo funkcji wariadycznej, floatjest ona przekazywana jako a double. Więc twój program jest równoważny zadeklarowaniu ajako 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 doubleokazały się równe 0, co może być powodem, dla którego otrzymałeś 0wynik swojego printfwywoł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
intargumenty są przekazywane w innych rejestrach niż doubleargumenty. printfwith %dprzyjmuje intargument, który %dwynosi 42, a drugi prawdopodobnie wypisuje śmieci, ponieważ nie było drugiego intargumentu.
Specyfikator %dmó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 floatrzeczywistoś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ę printfdrukuje ...
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 inti ośmiu bajtów double( floatjest konwertowany na doublewewnętrzny printf()), pierwsze cztery bajty abę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.