• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

C++ - Niedokładność zapisu float, dziwny błąd

Object Storage Arubacloud
0 głosów
331 wizyt
pytanie zadane 13 marca 2016 w C i C++ przez milosz503 Początkujący (270 p.)
edycja 13 marca 2016 przez milosz503

Dlaczego instrukcja warunkowa z wykorzystaniem float nie działa tak jak powinna?

Próbuję zrobić grę - prostą platformówkę z wykorzystaniem biblioteki SFML, napotkałem jednak problem z niepoprawnie działającą(?) instrukcją warunkową, podczas sprawdzania kolizji z wykorzystanie FloatRect[link]. Całkowicie nie wiem o co chodzi, ale według moich skromnych obliczeń if wykonuje się, kiedy w teorii nie są spełnione wszystkie warunki, co dziwne nie dzieje się tak za każdym razem, ale jakby losowo...

Metoda, w której występuje błąd:

bool Physics::intersects(FloatRect& rect1, FloatRect& rect2)
{
	std::cout << "Intersects:" << std::endl;
	std::cout << "___________________________________" << std::endl;

	std::cout << "Player: " << rect1.left << " " << rect1.top << " " << rect1.width << " " << rect1.height << std::endl;
	std::cout << "Blok: " << rect2.left << " " << rect2.top << " " << rect2.width << " " << rect2.height << std::endl;

	std::cout << "Warunki" << std::endl;
	std::cout << (rect1.left < (rect2.left + rect2.width)) << std::endl;
	std::cout << ((rect1.left + rect1.width) > rect2.left) << std::endl;
	std::cout << (rect1.top < (rect2.top + rect2.height)) << std::endl;
	std::cout << ((rect1.top + rect1.height) > rect2.top) << std::endl;




	if(		(rect1.left < (rect2.left + rect2.width)) &&
			((rect1.left + rect1.width) > rect2.left) &&
			(rect1.top < (rect2.top + rect2.height))  &&
			((rect1.top + rect1.height) > rect2.top)
		)
	{
		std::cout << "inter true" << std::endl;
		std::cout << "-----------------------------------" << std::endl;
		return true;
	}
	else
	{
		return false;
	}

}

Wyjście:

Intersects:
___________________________________
Player: 391.561 500 100 100
Blok: 400 400 100 100
Warunki
1
1
1
1
inter true:

 

Błąd występuje w trzecim warunku: (rect1.top < (rect2.top + rect2.height))

Gdzie:     rect1.top = 500,    rect2.top = 400,      rect2.height = 100
Warunek zwraca true, mimo, że 500 < (400 + 100) powinno chyba zwrócić false?...
 

Czy jest możliwość, że porównywanie dwóch float'ów, które pokazują tą samą wartość może nie wyjść prawidłowo? Przed porównaniem są wykonywane różne działania, typu dodawanie małych wartości(następny ruch, grawitacja) .

Próbowałem używać metody intersects, niestety także wykrywała czasem kolizje, kiedy jej nie było.

Nad tym problemem męczę się już od długiego czasu i ciągle całkowicie nie wiem o co chodzi. Za każdą próbę pomocy będę niesamowicie wdzięczny.

1 odpowiedź

+2 głosów
odpowiedź 14 marca 2016 przez Marcin Pietraszek Użytkownik (920 p.)
Tytułem wstępu - nie jestem programistą C++

Tak jest taka możliwość. C++ może używać standardu IEEE754 do reprezentacji liczb zmiennoprzecinkowych. Standard ten nie jest w stanie poprawnie przedstawić wszystkich wartości ułamkowych (np część z nich ma nieskończone rozwinięcie).

W związku z tą "ułomnością" liczb zmiennoprzecinkowych nie powinniśmy porównywać "wprost". Powinny być one porównywane z tak zwaną deltą, czyli błędem, który akceptujesz.

if liczb1 - liczba2 > delta

W Javie istnieje typ BigDecimal, który jest w stanie dokładnie reprezentować liczby zmiennoprzecinkowe. Zakładam, że analogicznie jest to rozwiązane w dodatkowych bibliotekach dla C++ w stylu boost: http://www.boost.org/doc/libs/master/libs/test/doc/html/boost_test/testing_tools/extended_comparison/floating_point.html
komentarz 15 marca 2016 przez milosz503 Początkujący (270 p.)
Dzięki za odpowiedź.
Wiem, że istnieje coś takiego jak niedokładność zapisu, ale zawsze jak o tym czytałem było przedstawiane jako widoczna różnica, po jakiś działaniach, np po dodaniu 0.1. W moim programie nie ma widocznej różnicy, a warunek, który niepoprawnie się wykonuje, składa się z liczb, które po wyświetleniu w konsoli nie mają części ułamkowej.
Przepisując z konsolki wartości, warunek powinien wyglądać tak: 500 < (400 + 100)  (co dziwne te wartości, są pokazane zawsze takie same, a warunek czasem zwraca true lub false).

Czy jest możliwość, że float jest z pozoru(przy normalnym wyświetlaniu) jakąś liczbą, ale przy porównywaniu takiej samej z pozoru wartości zwraca niepoprawną odpowiedź?
komentarz 15 marca 2016 przez Marcin Pietraszek Użytkownik (920 p.)
Jest taka możliwość, że wartość na konsoli jest zaokrąglona, przy porównaniach różnica jednak jest istotna.

Podobne pytania

0 głosów
1 odpowiedź 247 wizyt
pytanie zadane 27 czerwca 2018 w C# przez DODO Bywalec (2,950 p.)
0 głosów
1 odpowiedź 147 wizyt
pytanie zadane 2 kwietnia 2016 w HTML i CSS przez Karol Karol Początkujący (250 p.)
0 głosów
2 odpowiedzi 330 wizyt
pytanie zadane 20 marca 2020 w C i C++ przez wall7489 Obywatel (1,250 p.)

92,556 zapytań

141,404 odpowiedzi

319,560 komentarzy

61,942 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...