Dlaczego nie działa rzutowanie z int na double?

0

Piszę program zamieniający minuty na stopnie oraz sekundy na stopnie. Chce to zrobić przez rzutowanie, ale kompilator wynik pokazuje jako typ całkowitoliczbowy. Jeśli natomiast zmienię int na double/float wszystko działa poprawnie.

#include <iostream>
#include <iomanip>

using namespace std;

int main()
{
    double stopnie = 0, minuty = 0, sekundy = 0;
    int liczba_minut_na_stopnie = 0, liczba_sekund_na_stopnie = 0, ostateczna_liczba_stopni = 0;

    cout << "Podaj dlugosc w stopniach, minutach i sekundach: " << endl;
    cout << "Najpierw podaj stopnie ";
    cin >> stopnie;
    cout << "Nastepnie podaj minuty stopnia luku: ";
    cin >> minuty;
    cout << "Na koniec podaj sekundy luku: ";
    cin >> sekundy;

    liczba_minut_na_stopnie = (minuty / 60);
    liczba_sekund_na_stopnie = (sekundy / 3600);
    static_cast<double> (liczba_minut_na_stopnie);
    static_cast<double>  (liczba_sekund_na_stopnie);

    ostateczna_liczba_stopni = stopnie + liczba_minut_na_stopnie + liczba_sekund_na_stopnie;
    static_cast<double> (ostateczna_liczba_stopni);

    cout << fixed;
    cout << setprecision(4) << ostateczna_liczba_stopni << " stopni";

    return 0;
}
0

Jeśli przypisujesz wartość do typu całkowitego, to dlaczego zaskoczeniem jest, że jest ona typu całkowitego?

3
  1. static_cast nie działa na zmiennej, którą przekazujesz, czyli nie modyfikuje liczba_minut_na_stopnie. static_cast zwraca wartość jako nowy typ, czyli trzeba czegoś w rodzaju
    double xyz = static_cast<double>(liczba_minut_na_stopnie);

    Aczkolwiek w tym przypadku (rzutowanie między typami wbudowanymi) static_cast jest zbędny, wystarczy zwykłe przypisywanie

    double xyz = liczba_minut_na_stopnie;
  2. rzutowanie nie działa wstecz, czyli
    liczba_minut_na_stopnie = (minuty / 60);

    w tym momencie wartość została już obcięta z części dziesiętnej, żeby być przypisana do int, więc potem żadne rzutowanie już nie pomoże.

0

Wiesz jak sa zapisywane liczby w komputerze? W takiej kolejnosci to nie mialo prawa dzialac

3

@Magda Pietrzykowska: Operator rzutowania nie działa tak jak Ci się wydaje.
Nawet gdyby tak działał - to niewiele by to zmieniło w Twoim programie.

To są przykłady, które pokazuję na jednym z pierwszych wykładów z podstaw programowania - mam nadzieję, że pomogą Ci to zrozumieć.

Co wypisze ten program ?

int main()
{
  int a;
  a = 1 / 3;
  cout << a << endl;
}

Przewiń na dół jak już zdecydujesz:
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

Oczywiście zero.
Pytanie - dlaczego ?
Przewiń na dół jak już zdecydujesz:
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

Nie, przyczyną nie jest to, że zmienna a jest typu int (być może się pomyliłem, ale zgaduję Twoją odpowiedź na podstawie statystyki odpowiedzi moich studentów ;) ). Zobacz - jeśli zmienię na float to nadal wynikiem będzie zero!

int main()
{
  float a;
  a = 1 / 3;
  cout << a << endl;
}

W obu wypadkach program wypisze zero, bo zapis c++ a = 1 / 3; oznacza tak naprawdę DWIE operacje:

  • obliczenie wartości wyrażenia 1/3
  • wstawienie obliczonej wartości do zmiennej a

Teraz pytanie - jak komputer liczy wartość wyrażenia typu 1/3 ? W przypadku operatorów arytmetycznych możemy mieć do czynienia z jedną z trzech "podstawowych" sytuacji:
a) oba operandy są liczbami całkowitymi
b) oba operandy są liczbami zmiennoprzecinkowymi
c) jeden operand jest liczbą całkowitą a drugi - zmiennoprzecinkową

W sytuacji a) i b) - kompilator ma "łatwe" zadanie - po prostu bezpośrednio "zleca" wykonanie operacji procesorowi. Procesor posiada osobne instrukcje i wewnętrzne struktury służące do obsługi liczb całkowitych i zmiennoprzecinkowych. W przypadku gdy wykonujemy operację na liczbach całkowitych - jest ona w całości wykonywana przez "całkowitoliczbową" część procesora. W efekcie - wynikiem będzie tylko i wyłącznie liczba całkowita.
Gdyby oba operandy były zmiennoprzecinkowe - wynik byłby zmiennoprzecinkowy.
Jeśli jeden z operandów jest liczbą całkowitą a drugi zmiennoprzecinkową - kompilator ma "problem", bo procesor nie posiada bezpośrednio instrukcji pozwalających na wykonanie takich operacji. W tej sytuacji konieczne jest wykonanie konwersji, tak żeby oba operandy miały ten sam typ. W przypadku C/C++ kompilator wykona niejawną konwersję argumentu całkowitoliczbowego do typu zmiennoprzecinkowego.

W przypadku dzielenia 1 / 3 mamy do czynienia z przypadkiem a) - wynikiem będzie zatem zero i NIC WIĘCEJ. To nie będzie jedna trzecia zaokrąglona do liczby całkowitej, to nie będzie "zero i jeden reszty" - to będzie tylko i wyłącznie zero.

I teraz to zero wstawiamy do zmiennej "a". Jeśli zmienna a jest typu int - to komputer po prostu wstawi do niej obliczony wynik (zero całkowitoliczbowe).
Gdyby zmienna a była typu float, to komputer najpierw wykona niejawną konwersję tego "całkowitego" zera do zera zmiennoprzecinkowego.

Jeśli chcesz mieć "dobry" wynik, to musisz zrobić dwie rzeczy

  1. zadeklarować zmienną w której przechowywany będzie wynik jako float/double
  2. zmusić kompilator do wykonania operacji "zmiennoprzecinkowo"

Jak to zrobić ? Na przykład poprzez wymuszenie konwersji jednego z operandów do typu zmiennoprzecinkowego

int main()
{ 
  float a;
  a = 1.0 / 3;                                // OK, dzielenie double/int --> kompilator zrobi z tego double/double, potem przekonwertuje wynik na float i wstawi go do a
  a = static_cast<float> ( 1 ) / 3;    // OK, jawnie konwertujemy wartość 1 na typ float, w efekcie mamy float/int, kompilator zrobi z tego float/float
  a = static_cast<float> ( 1 / 3 );   // ŹLE - tu konwertujemy wynik działania a nie jeden z operandów. 
}

Pomijając to wszystko co do tej pory napisałem - zapis

static_cast<float> ( a );

nie ma większego "sensu". On nie służy do zmiany typu zmiennej, tylko (tak jak napisał Ci to @twonek) wyciąga wartość ze zmiennej "a" i konwertuje ją na float. Jest to jednak wartość "tymczasowa" - zniknie jeśli natychmiast jej nie zapamiętasz lub nie użyjesz w jakimś wyrażeniu.

Przy okazji z ciekawości zapytam - jak sądzisz - co wyświetli ten program (odpowiedz bez kompilowania i uruchamiania) ?

int main()
{
  int x = 2;
  int y = 3 * x;
  x = 5;
  cout << y << endl;
}

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0