Jak już było powiedziano w 1 rozdziale tej lekcji, konwersja ukryta (niejawna) jest realizowana samodzielnie przez kompilator MQL4. Programista nie musi opisywać algorytmu konwersji, wystarczy że rozumie jak to działa. Zobaczmy to na przykładach.
W poniższym kodzie 1 pokazany jest przykład konwersji jawnej i ukrytej wartości typu int na long.
#property strict
void OnStart()
{
int Int_value = 1000;
long Long_value_1 = (long)Int_value; // konwersja jawna z int na long
long Long_value_2 = Int_value; // konwersja ukryta z int na long
}
W tym skrypcie wartość zmiennej Int_value jest przypisywana dwóm zmiennym Long_value_1 i Long_value_2. W pierwszym przypadku programista w jawny sposób dał do zrozumienia kompilatorowi, że liczbę 1000 należy przekonwertować na typ long. W drugim przypadku wartość typu int po prostu jest przypisywana zmiennej typu long, bez jakichkolwiek wskazówek dla kompilatora. Teraz to kompilator samodzielnie podejmuje decyzję co z tym fantem zrobić. Rozumie to tak - jeśli coś jest przypisywane zmiennej typu long to ta wartość, niezależnie od jej poprzedniego typu, musi być przekształcona na long.
Wynikiem działania tego skryptu będzie utworzenie w pamięci RAM komórki o rozmiarze 4 bajtów, gdzie zostanie zapisana liczba 1000 typu int, a adresem tej komórki będzie nazwa zmiennej Int_value. Następnie, w RAM zostaną utworzone 2 komórki o rozmiarze 8 bajtów typu long. W każdej z nich po konwersji zostaną zapisane liczby 1000, a ich adresami będą nazwy zmiennych Long_value_1 i Long_value_2.
Teraz spróbujmy przekonwertować wartość liczbową do typu o mniejszym zakresie przechowywania liczb, np. z int do short (kod 2).
#property strict
void OnStart()
{
int Int_value = 1000;
short Short_value_1 = (short)Int_value; // konwersja jawna z int na short
short Short_value_2 = Int_value; // konwersja ukryta z int na short
}
Po kompilacji kodu tego skryptu, w MetaEditor 4 w logach zobaczymy ostrzeżenie: "possible loss of data due to type conversion", tj. możliwość utraty danych ze względu na rodzaj konwersji (rys. 1).
Rys. 1. Konwersja ukryta.
Jeśli klikniesz szybko 2 razy na to ostrzeżenie, kursor przejdzie do linijki kodu z ukrytą konwersją. Dlaczego kompilator wyświetlił to ostrzeżenie i dlaczego nic podobnego nie pojawiło się w przypadku kodu 1? Chodzi tu przede wszystkim o możliwość utraty danych podczas konwersji, o czym pisałem w rozdziale 7.2. Konwersja jawna. W przypadku konwersji ukrytej, jeśli wartość liczbowa jest konwertowana do nowego typu, który służy do przechowywania liczb o mniejszym zakresie, kompilator widzi możliwość utraty danych.
Jak to wygląda w przypadku ukrytej konwersji w kodzie 2? Zmienna Int_value ma typ int, dlatego można jej przypisać liczbę z zakresu od (minus) -2 147 483 648 do 2 147 483 647. Liczba z tego zakresu musi być przypisana zmiennej Short_value_2 typu short, która może przechowywać liczbę z mniejszego zakresu od (minus) -32 768 do 32 767. My wiemy, że liczba 1000 mieści się w granicach obu zakresów, jednak kompilator analizuje tylko typy i dlatego ostrzega programistę o potencjalnej możliwości utraty danych. W przypadku kodu 1 wartość liczbowa z typu int jest konwertowana do typu o większym zakresie long (od (minus) -9 223 372 036 854 775 808 do 9 223 372 036 854 775 807), dlatego utrata danych jest wykluczona i nie ma przed czym ostrzegać.
W przypadku konwersji jawnej, niezależnie od kierunków konwersji, kompilator o niczym nie będzie ostrzegać, dlatego że widzi że programista jest świadom tego co robi. W związku z tym, w kodzie 2 konwersja jawna z int do short nie wprowadzi kompilator w zamieszanie.
W przypadku, jeśli dwie wartości liczbowe są połączone operatorem binarnym: + (plus), - (minus), / (dzielenie) lub * (mnożenie), to przed takim działaniem matematycznym wartość niższego typu jest konwertowana do wyższego typu zgodnie z poniższym schematem (rys. 2):
Rys. 2. Domyślna konwersja typów.
Zobaczmy to na poniższych przykładach (kod 3).
#property strict
void OnStart()
{
char Char_value = 1;
//--- przykład 1
double Double_value_1 = Char_value / 2 + 0.35; // 1/2 + 0.35 = 0 + 0.35 = 0.35
Print("1 / 2 + 0.35 = ", Double_value_1);
//--- przykład 2
double Double_value_2 = Char_value / 2.0 + 0.35; // 1/2.0 + 0.35 = 0.5 + 0.35 = 0.85
Print("1 / 2.0 + 0.35 = ", Double_value_2);
}
Obliczenie Double_value_1 składa się z 2 operacji: najpierw zmienna Char_value dzielona jest przez 2 (Char_value / 2), a następnie do tego wyniku dodaje się 0.35.
Char_value typu char jest konwertowana do int, ponieważ drugi człon operacji dzielenia, stała 2, ma wyższy typ int. W MQL4 wszystkie liczby całkowite jako stałe mają domyślny typ int. Zgodnie z zasadą matematyki, wynikiem dzielenia 1 przez 2 powinno być 0.5. Trzeba jednak wziąć pod uwagę to, że w środowisku informatycznym wynikiem tego działania powinna być liczba typu int, dlatego przy konwersji liczby zmiennoprzecinkowej do całkowitej, część dziesiętna jest odrzucana. Wskutek czego zamiast 0.5 mamy 0.
W drugiej operacji dodawania, drugim członem jest stała 0.35 typu double. W MQL4 wszystkie liczby zmiennoprzecinkowe jako stałe mają domyślny typ double. W hierarchii konwersji typów (rys. 2) ten typ stoi wyżej, niż pierwszy człon 0 typu int, dlatego int jest konwertowany do double i zamiast 0 otrzymujemy 0.0. Ostateczny wynik to 0.0 + 0.35 = 0.35 (rys 3).
Podobnie do przykładu 1, obliczenie Double_value_2 składa się z 2 operacji: Char_value dzielona jest przez 2.0 (Char_value / 2.0), a potem dodawana jest liczba 0.35.
Tutaj stała 2.0 ma typ double, dlatego w pierwszej operacji dzielenia wartość zmiennej Char_value typu char jest konwertowana do wyższego typu double, tj. 1/2.0 = 1.0/2.0 = 0.5. W drugiej operacji dodawania oba człony mają typy double, dlatego tutaj nie ma żadnej konwersji. Ostateczny wynik to 0.5 + 0.35 = 0.85 (rys 3).
Rys. 3. Wynik działania skryptu.
Na podstawie powyższych przykładów widzimy, że wiedza nt konwersji typów ma duże znaczenie przy tworzeniu programów w MQL4. Szczególną uwagę chcę zwrócić na ukryte konwersje związane z typem double. Na przykładzie kodu 3 widać, że brak zera po kropce w liczbach zmiennoprzecinkowych może znacząco zmienić wynik.