7. Konwersja typów zmiennych.

7.2. Konwersja jawna

Przy konwersji jawnej programista w kontrolowany sposób dokonuje przemiany typu zmiennej. Schemat takiej konwersji wygląda w następujący sposób (kod 1).

Kod 1
variable_1 = (nowy_typ)variable_2;

Dopuszczalny jest rownież następujący zapis konwersji jawnej (kod 2).

Kod 2
variable_1 = nowy_typ(variable_2);

Jako variable_2 może występować zmienna lub np. wynik obliczenia matematycznego lub działania funkcji (kod 3).

Kod 3
#property strict

void OnStart()
  {
   int   Int_value;
   long  Long_value;
   short Short_value = 5555;

   Int_value  = (int)Short_value;      /* konwersja jawna zmiennej Short_value do typu int
                                          i przypisanie wartości innej zmiennej Int_value */


   Long_value = (long)MathMax(1, 33);  /* funkcja MathMax znajduje maksymalną wartość
                                          z dwóch liczb (1 i 33), następnie ta liczba
                                          w jawny sposób zostaje przekonwertowana na typ long */


   Print("Int_value = ", Int_value);
   Print("Long_value = ", Long_value);
  }

W poprzednim rozdziale zaznaczyłem, że w niektórych przypadkach konwersji możliwa jest utrata informacji. Chodzi tu przede wszystkim o to, że jeśli spróbujemy "zapakować" liczbę do typu, który nie służy do przechowywania tej liczby w pamięci RAM, to komputer przypisze zmiennej niepoprawną wartość (kod 4).

Kod 4
#property strict

void OnStart()
  {
   char   Char_value_1   = -111;  // liczba -111 mieści się w granicach typu char (od -128 do 127)
   char   Char_value_2   = 222;   // 222 nie mieści się w granicach typu char
   ushort Ushort_value_1 = -111;  // -111 nie mieści się w granicach typu ushort (od 0 do 65535)
   ushort Ushort_value_2 = 50000; // 50000 mieści się w granicach typu ushort

   Print("Char_value_1   = ", Char_value_1);
   Print("Char_value_2   = ", Char_value_2);
   Print("Ushort_value_1 = ", Ushort_value_1);
   Print("Ushort_value_2 = ", Ushort_value_2);
  }

Po uruchomieniu tego skryptu w MetaTrader4 w logach terminala zobaczymy następujące komunikaty (rys 1).

Rys. 1. Konwersja liczb typów całkowitych.


Tutaj w przypadku zmiennych Char_value_2 i Ushort_value_1 widzimy efekt utraty informacji. W kodzie źródłowym Char_value_2 spróbowaliśmy przypisać liczbę 222, która nie mieści się w granicach typu char (od -128 do 127) i program wyświetlił wartość (minus) -34. Z kolei zmiennej Ushort_value_1 spróbowaliśmy przypisać liczbę -111, też spoza zakresu obsługiwanego przez typ ushort (od 0 do 65535), w skutek czego utracono prawidłową wartość i program wyświetlił inną wartość 65425.


Taki samy efekt utraty informacji można otrzymać w procesie konwersji liczby do innego typu o niewłaściwym zakresie (kod 5).

Kod 5
#property strict

void OnStart()
  {
   char   Char_value   = -111;
   ushort Ushort_value = (ushort)Char_value; // konwersja jawna liczby typu char do typu ushort
   short  Short_value  = (short)Char_value;  // konwersja jawna liczby typu char do typu short

   Print("Char_value   = ", Char_value);
   Print("Ushort_value = ", Ushort_value);
   Print("Short_value  = ", Short_value);
  }

W powyższym skrypcie podjęto próbę konwersji liczby -111 typu char do typów ushort i short. Wiemy już, że typ ushort nie służy do przechowywania liczb ujemnych, dlatego po uruchomieniu tego skryptu w MT4 w logach terminala znowu zobaczymy nieprawidłową liczbę 65425 (rys. 2). Z kolei konwersja do typu short przebiegnie prawidłowo i zobaczymy liczbę -111.

Rys. 2. Konwersja liczb typów całkowitych.


Nie zawsze konwersja liczby z jednego typu do innego typu o mniejszym zakresie musi się skończyć utratą informacji. W kolejnym przykładzie (kod 6) liczbę 50000 typu ulong przemienimy na pozostałe typy liczb całkowitych.

Kod 6
#property strict

void OnStart()
  {
   ulong  Ulong_value  = 50000;

   long   Long_value   = (long)Ulong_value;   // ok
   uint   Uint_value   = (uint)Ulong_value;   // ok
   int    Int_value    = (int)Ulong_value;    // ok
   ushort Ushort_value = (ushort)Ulong_value; // ok

   short  Short_value  = (short)Ulong_value;  /* 50000 nie mieści się w granicach
                                                 typu short (od -32768 do 32767) */

   uchar  Uchar_value  = (uchar)Ulong_value;  /* 50000 nie mieści się w granicach
                                                 typu uchar (od 0 do 255) */

   char   Char_value   = (char)Ulong_value;   /* 50000 nie mieści się w granicach
                                                 typu char (od -128 do 127) */


   Print("Ulong_value  = ", Ulong_value);
   Print("Long_value   = ", Long_value);
   Print("Uint_value   = ", Uint_value);
   Print("Int_value    = ", Int_value);
   Print("Ushort_value = ", Ushort_value);
   Print("Short_value  = ", Short_value);
   Print("Uchar_value  = ", Uchar_value);
   Print("Char_value   = ", Char_value);
  }

Na rysunku 3 widzimy wynik działania tych konwersji.

Rys. 3. Konwersja liczb typów całkowitych.


Widzimy, że problem z utratą informacji pojawia się tylko wtedy, gdy próbujemy "zapakować" liczbę do typu, który nie służy do przechowywania tej liczby. W powyższym przykładzie liczba 50000 nie pasuje do typów short, uchar i char.


Jeśli zaistnieje potrzeba konwersji liczby typu całkowitego do typu zmiennoprzecinkowego, to proponuję robić to do typu double (kod 7).

Kod 7
#property strict

void OnStart()
  {
   int    Int_value    = 123456789;
   float  Float_value  = (float)Int_value;  // konwersja jawna int do float
   double Double_value = (double)Int_value; // konwersja jawna int do double

   Print("Int_value    = ", Int_value);
   Print("Float_value  = ", Float_value);
   Print("Double_value = ", Double_value);
  }

Tutaj typ float nie poradził sobie dobrze z obróbką liczby 123456789, a double prawidłowo wykonał to zadanie (rys 4).

Rys. 4. Konwersja liczby typu całkowitego do typy liczby zmiennoprzecinkowej.


W przypadku konwersji liczby zmiennoprzecinkowej do liczby całkowitej, część dziesiętna zawsze jest odrzucana. Jeśli trzeba zaokrąglić liczbę zmiennoprzecinkową do najbliższej liczby całkowitej wtedy należy użyć funkcji MathRound() (kod 8).

Kod 8
#property strict

void OnStart()
  {
   double A  = 1.99999;
   double B  = -543.21;
  
   int    CC = (int)A;
   int    DD = (int)B;
   int    EE = (int)MathRound(A);
   int    GG = (int)MathRound(B);

   Print("Konwersja: CC = ", CC);
   Print("Konwersja: DD = ", DD);
   Print("Zaokrąglenie i konwersja: EE = ", EE);
   Print("Zaokrąglenie i konwersja: GG = ", GG);
  }

W powyższym skrypcie podczas konwersji liczb 1.99999 oraz -543.21 z typu double do typu int część dziesiętna zostanie odrzucona i nowym zmiennym zostanie przypisana tylko część całkowita tych liczb (CC = 1, DD = -543). Z kolei funkcja MathRound() zaokrągli 1.99999 do 2.0, a potem podczas jawnej konwersji do int liczba 2.0 zostanie przerobione na 2 (EE = 2). W przypadku -543.21 funkcja zaokrągli tę liczbę do -543.0, która następnie zostanie przerobiona na -543 (GG = -543) (rys 5).

Rys. 5. Konwersja liczby typu zmiennoprzecinkowego do typy liczby całkowitej.