Arduino Polska Forum
Ciekawy problem ze zmienną - Wersja do druku

+- Arduino Polska Forum (https://forum.arduinopolska.pl)
+-- Dział: Korzystanie z Arduino (https://forum.arduinopolska.pl/dzial-korzystanie-z-arduino)
+--- Dział: Instalacja i rozwiązywanie problemów (https://forum.arduinopolska.pl/dzial-instalacja-i-rozwi%C4%85zywanie-problem%C3%B3w)
+--- Wątek: Ciekawy problem ze zmienną (/watek-ciekawy-problem-ze-zmienn%C4%85)



Ciekawy problem ze zmienną - kilof206 - 01-11-2017

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


RE: Ciekawy problem ze zmienną - Robson Kerman - 01-11-2017

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.