Arduino Polska Forum

Pełna wersja: Ciekawy problem ze zmienną
Aktualnie przeglądasz uproszczoną wersję forum. Kliknij tutaj, by zobaczyć wersję z pełnym formatowaniem.
Cześć.

Napotakłem na ciekawy problem. Czy ktoś go może wytłumaczyć? A jak temu zapobiec?
W wyniku uruchomienia poniższego kodu, w monitorze dostajemy dwa wyniki (oba poprawne): 24000;


Kod:
long z;

void setup()
{
  Serial.begin(9600);
  
  unsigned long x = func(6000, 24, 48);
  Serial.println(x);

  z = 48;

  unsigned long y = func(6000, 24, z);
  Serial.println(y);
}

unsigned long func(unsigned long a, int b, long c)
{
  return a * (pow(2, (c / (float)b)));
}

void loop()
{
}


Jeśli natomiast przypisanie wartości do zmiennej z=48 przeniesiemy w kodzie nieco wyżej, zaraz pod Serial.begin, czyli: 


Kod:
void setup()
{
  Serial.begin(9600);
  z = 48;
  
  unsigned long x = func(6000, 24, 48);
  Serial.println(x);

  unsigned long y = func(6000, 24, z);
  Serial.println(y);
}

w monitorze zobaczymy: 
24000
23999

Dlaczego druga wartość jest błędna?

Zauważyłem również, że taka konstrukcja da poprawne wyniki:


Kod:
void setup()
{
  Serial.begin(9600);
  z = 48;
  
  unsigned long x = func(6000, 24, 48);
  unsigned long y = func(6000, 24, z);

  Serial.println(x);
  Serial.println(y);
}


jak i również zadeklarowanie z wewnątrz setup() również poprawnie wyświetli wartości, czyli:


Kod:
void setup()
{
  Serial.begin(9600);
  long z = 48;
  
  unsigned long x = func(6000, 24, 48);
  Serial.println(x);

  unsigned long y = func(6000, 24, z);
  Serial.println(y);
}


Testy przeprowadzałem na Arduino UNO w IDE 1.8.5.

Pozdrawiam, Marcin
W funkcji func() następuje konwersja typu int na float, a funkcja zwraca unsigned long.
Dzielenie c/(float)b daje wynik przybliżony, coś w postaci 1.9999999, więc wynik potęgowania to 3.9999997,
a 6000*3.9999997=23999.9982.
Konwersja do long zwraca część całkowitą wyniku, czyli 23999.
A dla czego tak się dzieje, że w zależności gdzie zadeklarujesz, lub gdzie przypiszesz zmiennej z wartość, to otrzymujesz różne wyniki?
Jest to automatyczne rzutowanie typów zadeklarowanych zmiennych w zależności od tego, co kompilator uzna za stosowne. Kompilator konwertuje typy zawsze gdy jest to tylko możliwe i gdy już wie, że będzie dzielenie long przez float, bo użyłeś funkcji func(), to nie będzie rzutował c na float, bo wie że wyniki będą nieprawdziwe.
W normalnym kompilatorze powinno wyskoczyć ostrzeżenie, że wyniki będą przybliżone, chociaż pewnie GCC nie popełnił by tego błędu, bo inna jest kolejność kompilacji.