#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
.
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 afloat
jest konwertowany nadouble
.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 adouble
. Więc twój program jest równoważny zadeklarowaniua
jakodouble
.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 swojegoprintf
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
źródło
int
argumenty są przekazywane w innych rejestrach niżdouble
argumenty.printf
with%d
przyjmujeint
argument, który%d
wynosi 42, a drugi prawdopodobnie wypisuje śmieci, ponieważ nie było drugiegoint
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 zeroBinarna 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.
źródło
Dzieje się tak z powodu reprezentacji liczby zmiennoprzecinkowej w systemie binarnym. Konwersja na liczbę całkowitą pozostawia 0.
źródło
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.
źródło
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 anint
(i to właśnie z nim mówisz%d
), to on ci wierzy i zajmuje tylko bajty potrzebne doint
. W tym przypadku, zakładając, że twój komputer używa czterobajtówint
i ośmiu bajtówdouble
(float
jest konwertowany nadouble
wewnętrznyprintf()
), pierwsze cztery bajtya
będą po prostu zerami, a to zostanie wydrukowane.źródło
Nie konwertuje automatycznie liczby zmiennoprzecinkowej na liczbę całkowitą. Ponieważ oba mają inny format przechowywania. Więc jeśli chcesz konwertować, użyj (int) typecasting.
#include <stdio.h> int main() { float a = 1234.5f; printf("%d\n", (int)a); return 0; }
źródło
Ponieważ otagowałeś go również C ++, ten kod wykonuje konwersję zgodnie z oczekiwaniami:
#include <iostream.h> int main() { float a = 1234.5f; std::cout << a << " " << (int)a << "\n"; return 0; }
Wynik:
1234.5 1234
źródło
%d
jest dziesiętna%f
jest pływającyzobacz więcej tutaj .
Otrzymujesz 0, ponieważ liczby zmiennoprzecinkowe i liczby całkowite są reprezentowane inaczej.
źródło
Wystarczy użyć odpowiedniego specyfikatora formatu (% d,% f,% s, itd.) Z odpowiednim typem danych (int, float, string itp.).
źródło
Chcesz% f, a nie% d
źródło
hej, musiało coś wydrukować, więc wypisało 0. Pamiętaj, że w C 0 jest wszystko inne!
źródło
To nie jest liczba całkowita. Spróbuj użyć
%f
.źródło