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

Odpowiedz Nowy wątek
2017-04-03 11:47

Rejestracja: 3 lata temu

Ostatnio: 2 lata temu

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";
}
edytowany 1x, ostatnio: Azreal, 2017-04-03 14:49

Pozostało 580 znaków

kq
2017-04-03 11:57
kq
Moderator C/C++

Rejestracja: 6 lat temu

Ostatnio: 1 minuta temu

Lokalizacja: Szczecin

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 [-π, π].


edytowany 1x, ostatnio: kq, 2017-04-03 11:57

Pozostało 580 znaków

2017-04-03 12:15

Rejestracja: 3 lata temu

Ostatnio: 2 lata temu

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>

Pozostało 580 znaków

kq
2017-04-03 12:18
kq
Moderator C/C++

Rejestracja: 6 lat temu

Ostatnio: 1 minuta temu

Lokalizacja: Szczecin

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?


Pozostało 580 znaków

2017-04-03 12:40
Moderator C/C++

Rejestracja: 3 lata temu

Ostatnio: 2 lata temu

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."

Pozostało 580 znaków

kq
2017-04-03 12:52
kq
Moderator C/C++

Rejestracja: 6 lat temu

Ostatnio: 1 minuta temu

Lokalizacja: Szczecin

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.


Pozostało 580 znaków

2017-04-03 13:33

Rejestracja: 3 lata temu

Ostatnio: 2 lata temu

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.

Pozostało 580 znaków

kq
2017-04-03 13:39
kq
Moderator C/C++

Rejestracja: 6 lat temu

Ostatnio: 1 minuta temu

Lokalizacja: Szczecin

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:


Masz racje, pomyliłem własności trójkątów. Moje pytanie w zasadzie miało błąd natury matematycznie. Czy jest możliwe usunięcie posta z pytaniem? - Azreal 2017-04-03 14:19

Pozostało 580 znaków

Odpowiedz

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