problem z dokładnością funkcji trygonometycznych w C+

0

Witam
Piszę program konwertujący współrzędne kartezjańskie na biegunowe. Wszystko działa, ale dokładność wyników jest dość słaba.
np. x = 3 , y = 4 powinno dawać R = 5 alfa = 60 stopni a daje 53,1301
podejrzewam niedokładność funkcji trygonometrycznych załączonych w bibliotece.
Poniżej kod

// structfun_X.cpp -- (137) - listing 7.12 - funkcje majace strukture jako parametr
// program przeliczajacy uklad wspolzednych biegunowych na prostokatne i odwrotnie
// poprawic dokladnosc obliczen uzyc  long double i zaokraglen oraz przelicznika z Pi

#define _USE_MATH_DEFINES
#include <iostream>
#include <cmath>			// biblioteka z funkcjami artmetycznymi
#include <cstdlib>			// exit();
//#include <math.h>
//#include <conio.h>

using namespace std;

const long double rad_to_dag = 57.29577951308767;	// przelicznik z radianow na stopnie
const long double dag_to_rad = 1 / rad_to_dag;		// przelicznik ze stopni na radiany
//const long double r_to_d = 180 / M_PI;			// daje mniejsza dokladnosc 

struct polar
{
	long double distance;		// odleglosc od poczatku ukladu
	long double angel;			// kat wzgledem dodatniej polosi X
};

struct rect
{
	long double x;				// odleglosc od poczatku ukaldu w pozimie
	long double y;				// odleglosc od poczatku ukaldu w pionie
};

polar rect_to_polar(rect xypos);
rect polar_to_rect(polar dapos);
void show_polar(polar dapos);
void show_rect(rect xypos);
void show_menu();

int main()
{
	using namespace std;
	show_menu();
	//cout << "r_to_d =" << r_to_d << endl;
	rect rplace;
	polar pplace;
	char temp;
	cin >> temp;

	while((temp!='q') && (temp!='Q'))
	{
			if (temp == 'R' || temp == 'r')
			{
			cout << "Wybrales konwersje z ukladu biegunowego na prostokatny.\n\n";
			cout << "Podaj odleglosc oraz kat: ";
				if (cin >> pplace.distance >> pplace.angel)
				{
				rplace = polar_to_rect(pplace);
				show_rect(rplace);
				cout << "Wpisz dowolna litere aby wrocic do manu wyboru.\n";
				}
			
			}
			else if (temp == 'P' || temp == 'p')
			{
			cout << " wybrales konwersje z ukladu prostokatnego na biegunowy.\n\n";
			cout << "Podaj wspolrzedna X oraz wspolrzedna Y ";
			if (cin >> rplace.x>> rplace.y)
				{
				pplace = rect_to_polar(rplace);
				show_polar(pplace);
				cout << "Wpisz dowolna litere aby wrocic do manu wyboru.\n";
				}

			}
			else if (temp == 'q' || temp == 'Q')
			{
			cout << "Program zaraz zostanie zmakniety.";
			cin.ignore(numeric_limits<streamsize>::max(), '\n');
			cin.get();
			exit(EXIT_FAILURE);
			}
			
		cin.ignore(numeric_limits<streamsize>::max(), '\n');
		cin.get();
		show_menu();
		cin >> temp;
	}
	
	cout << "Do zobaczenia :)";
	cin.ignore(numeric_limits<streamsize>::max(), '\n');
	cin.get();
	return 0;
}

polar rect_to_polar(rect xypos) 
{
	using namespace std;
	polar answer_p;

	answer_p.distance = sqrt(xypos.x * xypos.x + xypos.y * xypos.y);		//pierwiastek z sumy kwadartow
	answer_p.angel =  atan2(xypos.y , xypos.x);								// arcus tang y/x

	return answer_p;														//zwraca strukture polar
}

rect polar_to_rect(polar dapos)
{
	using namespace std;
	rect answer_r;

	answer_r.x = dapos.distance * sin(dapos.angle*dag_to_rad);			// odleglosc * sin (kat przeliczonego stopni na radiany)
	answer_r.y = dapos.distance * cos(dapos.angle*dag_to_rad);			// odleglosc * cos (kat przeliczonego stopni na radiany)
	return answer_r;
}

void show_polar(polar dapos)
{
	using namespace std;

	cout << "Odleglosc  = " << dapos.distance;
	cout << ", kat w radianach =" << dapos.angle;
	cout << ", a kat w stopniach = " << dapos.angle*rad_to_dag << endl;
}

void show_rect(rect xypos)
{
	using namespace std;

	cout << "Wspolrzedna X = " << xypos.x;
	cout << ", a wspolrzedna Y = " << xypos.y << endl;
}

void show_menu()
{
	using namespace std;
	cout << "Wybierz rodzaj konwersji:\n\n";
	cout << "p) z ukladu prostokatnego na biegunowy.\n";
	cout << "r) z ukladu biegunowego na prostokatny.\n";
	cout << "q) zakoncz program.\n";
}
1

podejrzewam niedokładność funkcji trygonometrycznych załączonych w bibliotece.

Niedobry nawyk, trafić na błąd w typowym użyciu stdlib jest bardzo ciężko.

angel

To anioł. Kąt to angle.

cos(dapos.angel*dag_to_rad)

Dlaczego to wymnażasz kąt przez dag_to_rad? Do angel przypisujesz wynik atan2, który już jest w zakresie [-π, π].

0

Z tym anielskim kątem to fatalna literówka.
Mnożnik dag_to_rad ma przeliczać wejściowe stopnie na radiany podawanie do funkcji trygonometrycznej. (dapos.angle jest w stopniach od 0 do 360)
Mógłbyś rozwinąć co miałeś na myśli z nie dobrym nawykiem i błędem w <stdlib>

0
answer_p.angel =  atan2(xypos.y , xypos.x);

To jedyne miejsce gdzie widzę przypisanie do angel. Zalecam zapoznać się z dokumentacją std::atan2.

Biblioteka standardowa testowana jest przez miliony programistów w milionach programów (liczby z palca, pewnie jest więcej, szczególnie programów). Myślisz, że nikt by nie zauważył tak ogromnego błędu obliczeniowego w jednej z podstawowych funkcji matematycznych?

0

"Wartość z palca" zaczerpnąłem z podręcznika Stephena Praty "Język C++ szkoła programowania", bo 180/M_PI dawało mi mniej dokładna wartość przelicznika reg_to_dag.
Co do błędu w standardowej bibliotece zakładałem, że może używam jakiejś starej funkcji. (niemniej w przykładzie z książki kąt wynikowy to również 53.1301 stopni stąd moje zdziwienie)
ale przy odległości R=sqrt(2) i kącie alfa = 45 stopni podaje prawidło x = 1 oraz y=1 (konwersja daje poprawne wyniki w obu kierunkach)
W tej też książce była uwaga, że funkcja tan2 przyjmuje wartości w radianach dlatego też podany był mnożnik rad_to_dag.
i to by też potwierdzał Twój link i opis wartości zwracanej przez atan2:

If no errors occur, the arc tangent of y/x (arctan(y/x)) in the range [-π ; +π] radians, is returned."
0

Jak tam wolisz, po usunięciu zbędnego przemnożenia i zamianie błędnie ustalonych cos/sin u mnie działa.

PS: "wartość z palca" odnosiła się do milionów.

0

Dziękuję za poświęcony czas, ale Twój kod również daje wartość 53.1301 stopni zamiast 60 stopni, a przecież trójką Pitagorejski ma boki 3, 4, 5, i kąty 30, 60, 90.

1

trójką Pitagorejski ma boki 3, 4, 5, i kąty 30, 60, 90.

Nie, nie ma. Kąty sobie zmyśliłeś. Powiedz, czy widzisz pewien problem z poniższym trójkątem równobocznym:

1 użytkowników online, w tym zalogowanych: 0, gości: 1