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

Błędny wynik tangensa Javascript

Object Storage Arubacloud
+1 głos
275 wizyt
pytanie zadane 21 września 2021 w JavaScript przez Martita Bywalec (2,470 p.)

Dzień dobry jestem początkująca.

Zrobiłam kalkulator, który przelicza stopnie alfa na sinus alfa, cosinus alfa, tangens alfa. Przy wprowadzeniu liczbę 90 stopni przez użytkownika wynik tangensa powinien pokazać ∞ lub infity lub w miejsce wyniku nic nie powinno być (wynik nie istnieje). Natomiast mi wynik pokazuje błędny 16331239353195370 = 16331239353195370.0000 tangens alfa. Jeśli użytkownik wprowadzi liczbę 45 stopni to wynik pokazuje poprawny 1 = 1.0000 .

Proszę o pomoc i wskazanie gdzie jest błąd.

<!DOCTYPE html>
<html lang="pl">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="stopien_sinus.js"></script>
</head>

<body>
    <form id="oblicz" action="" method="post" autocomplete="off">
        <div>
            <h3>Przelicznik Stopnia Alfa na Sinus Alfa</h3>
            Stopień Alfa <input type="number" id="SA"><br>
            Sinus: <span id="SIN">= </span><br><br>
            Cosinus: <span id="KOS">= </span><br><br>
            Tangens: <span id="TAN">= </span><br><br>
            <input type="button" value="Oblicz" onclick="myFunction()">
            <input type="reset" value="RESETUJ" onclick="resetuj()">
        </div>
    </form>

</body>

</html>

 

function myFunction() {
const SA = parseInt(document.getElementById("SA").value);
const SIN = Math.sin(parseInt(SA) * Math.PI / 180);
const KOS = Math.round(Math.cos(parseInt(SA) * Math.PI / 180));
const TAN = Math.round(Math.tan(parseInt(SA) * Math.PI / 180));
    document.getElementById("SIN").innerHTML = Math.sin(SA * Math.PI / 180) + ' = ' + SIN.toFixed(4);
    document.getElementById("KOS").innerHTML = Math.round(Math.cos(SA * Math.PI / 180)) + ' = ' + KOS.toFixed(4);
    document.getElementById("TAN").innerHTML = Math.round(Math.tan(SA * Math.PI / 180)) + ' = ' + TAN.toFixed(4);
   
return false;
}

function resetuj () {

    document.getElementById("SIN").innerHTML = '';
    document.getElementById("KOS").innerHTML = '';
    document.getElementById("TAN").innerHTML = '';
   
}

 

3 odpowiedzi

+2 głosów
odpowiedź 26 września 2021 przez Bosswell Nałogowiec (36,470 p.)
wybrane 26 września 2021 przez Martita
 
Najlepsza

Dla przyszłych poszukiwaczy rozwiązanie wyciągnięte z komentarzy.
Przy wyznaczeniu nieskończoności tangensa dla konkretnych stopni, możemy skorzystać z jego okresowości w celu zredukowania stopni.

Dla 400
(int) 400 / 180 = 2
400 - 2 * 180 = 40
--------
Dla 270
(int) 270 / 180 = 1
270 - 1 * 180 = 90
--------
Dla -90
(int) -90 / 180 = 0
-90 - 0 * 180 = -90
--------
Dla -450
(int) -450 / 180 = -2
-450 - (-2 * 180) = -450 + 360 = -90
--------
a = stopnie;
b = parseInt(a / 180);
c = Math.abs(a - b * 180);

if (c === 90) {
   console.log("Brak rozwiązań");
}

Podane przez użytkownika stopnie, redukujemy przez jego okres 180 stopni. Po redukcji, tangens nie będzie miał rozwiązań dla 90 i -90 stopni.

Aplikacja rozwiązania do zadania:

function myFunction() {
  const container = document.getElementById("TAN");
  const deegres = parseInt(
    document.getElementById("SA").value
  );
 
  const normalizedDeegres = Math.abs(
    deegres - parseInt(deegres / 180) * 180
  );
 
  if (normalizedDeegres === 90){
      container.innerHTML = '<b>Nie istnieje! - &#8734</b>';
 
      return;
  }
 
  container.innerHTML = `<b>${Math.tan(deegres)}</b>`;
}

 

+1 głos
odpowiedź 21 września 2021 przez edutomek Dyskutant (8,380 p.)
Czyli działa (w miarę) poprawnie. Powinno być +inf, jest jakaś bardzo duża liczba.

Po pierwsze - Math.PI to nie jest liczba PI. To jest pewne przybliżenie liczby PI (której dokładnie nie da się zapisać, bo jest to liczba niewymierna). Mamy więc pierwsze źródło błędów numerycznych - operowanie na wartościach przybliżonych.

Wyliczając kąt w radianach nie wyliczymy dokładnie tego PI, PI/2 (innych kątów też nie), lecz jedynie ich pewne przybliżenia.

W zależności od tego, jakie to będą przybliżenia, otrzymamy mniej lub bardziej dokładne wyniki.

Nie wspominając o tym, jak ten tangens jest zaimplementowany. Gdzieś znalazłem informację, że jest to sin(x) / cos(x) - ale jak są zaimplementowane te sinusy i cosinusy? To są jakieś algorytmy numeryczne, a więc wyniki będą przybliżone (czasami lepiej, czasami gorzej) i obarczone błędami numerycznymi.

No i potem takie "kwiatki" wychodzą.

Zresztą nie trzeba funkcji trygonometrycznych, żeby doświadczyć problemów numerycznych: proponuję wpisać w JS a = 0.1, a potem sprawdzić wynik a + a + a (u mnie np. wcale nie wychodzi 0.3; co ciekawe, przy dalszym dodawaniu są poprawne wyniki, ale już przy ośmiu składnikach też nie mam 0.8). Można też popróbować z a = 0.01, a = 0.001... Ciekawskim polecam sprawdzić a = 0.125 i zastanowić się, dlaczego dla tej interesującej wartości akurat wszystko liczone jest poprawnie.

Generalnie, operując na liczbach zmiennoprzecinkowych, należy przyjąć pewną dokładność. Tzn. nie porównujemy wartości zmiennych do stałych, lecz sprawdzamy, czy wartość bezwzględna różnicy jest odpowiednio mała: if ( Math.abs(a - 2) < EPSILON ) ...

Podobnie można postąpić z tymi kątami. Nie wyliczymy dokładnie pewnych wartości (np. tych 45, 90, czy 180 stopni) - to może warto napisać własną funkcję, która dla tych specyficznych wartości (z pewną dokładnością) zwróci poprawny wynik?
komentarz 21 września 2021 przez Martita Bywalec (2,470 p.)

Wyniki mają wyjść według tej tabeli https://doza.pro/art/math/algebra/pl/sin i takie wychodzą przy sinus, cosinus, tangens ale przy tangens gdzie wynik powinien być + / - nieskończoność to wychodzą błędne wyniki. Usunęłam  Math.round .

Potrzebuje zrobić kod tak aby przy wynikach gdzie powinno być nieskończoność pojawiły się prawidłowe wyniki czyli + nieskończoność lub - nieskończoność.

Dopiero zaczęłam naukę programowania w JavaScript i jeszcze nie za bardzo wiem jak pisać własne funkcje. Math.abs też nie daje tego czego oczekuje.

Znalazłam kod http://jsfiddle.net/DanVicBez/j7LER/ który daje w tangens poprawny wynik gdy wynikiem powinno być nieskończoność, ale nie wiem jak go przekształcić aby pasowało do mojego kodu.

function myFunction() {
const SA = parseInt(document.getElementById("SA").value);
const SIN = Math.sin(parseInt(SA) * Math.PI / 180);
const KOS = Math.cos(parseInt(SA) * Math.PI / 180);
const TAN = Math.tan(parseInt(SA) * Math.PI / 180);
    document.getElementById("SIN").innerHTML = Math.sin(SA * Math.PI / 180) + ' = ' + SIN.toFixed(4);
    document.getElementById("KOS").innerHTML = Math.cos(SA * Math.PI / 180) + ' = ' + KOS.toFixed(4);
    document.getElementById("TAN").innerHTML = Math.tan(SA * Math.PI / 180) + ' = ' + TAN.toFixed(4);
   
return false;
}

function resetuj () {

    document.getElementById("SIN").innerHTML = '';
    document.getElementById("KOS").innerHTML = '';
    document.getElementById("TAN").innerHTML = '';
   
}

 

 

komentarz 21 września 2021 przez edutomek Dyskutant (8,380 p.)

Jeśli jest tak, jak myślę (i napisałem) - i problem wynika z faktu błędów numerycznych obliczeń, to wyjścia są dwa:

1) zastosować inne algorytmy numeryczne (dające mniejsze błędy, zapewne kosztem czasu obliczeń; pewnie to właśnie jest robione w podanym przez Ciebie przykładzie),
2) zastosować korektę wyniku.

W tym przypadku korekta polega na "zaokrągleniu" odpowiednio dużyć wartości do +/- nieskończoności.

Może uściślę: masz taką wartość (w stopniach), dla której wartość tangensa powinna być równa +/- nieskończoność, no ale wychodzi skończona. To może najprościej (i nieco bezczelnie) będzie napisać funkcję "zaokrąglającą":

const MY_INFINITY = 999999999;

function correctInf(value) {
  if ( value > MY_INFINITY )
    return Infinity;
  if ( value < - MY_INFINITY )
    return -Infinity;
  return value;
}

i zamiast Math.tan(...) używać correctInf( Math.tan(...) )
 

komentarz 21 września 2021 przez Martita Bywalec (2,470 p.)
Problem w tym że muszę używać document.getElementById i const. Zrobiłam z nimi pole kuli, prostokąta, trójkąta, kwadratu, sinus, cosinus i stanełam na tangensie.
komentarz 21 września 2021 przez edutomek Dyskutant (8,380 p.)
const TAN = Math.round(correctInf(Math.tan(parseInt(SA) * Math.PI / 180)));

document.getElementById("TAN").innerHTML = correctInf(Math.tan(SA * Math.PI / 180)) + ' = ' + TAN.toFixed(4);

 

komentarz 21 września 2021 przez Bosswell Nałogowiec (36,470 p.)
Zamiast robić obliczenia dla stopni, które nie dają wyników liczbowych, możnaby odrazu sprawdzić stopnie dla jakich liczymy tangensa i wypisać odpowiednio:

+nieskończoność dla (90 stopni + k*180 stopni) -> k należy do liczb całkowitych
-nieskończoność dla (-90 stopni + k*180 stopni) -> k należy do liczb całkowitych

Jeżeli nie są spełnione warunki, to standardowa funkcja tangensa.
komentarz 22 września 2021 przez Martita Bywalec (2,470 p.)

@edutomek, Nie wyświetla wyniku.

komentarz 22 września 2021 przez Martita Bywalec (2,470 p.)

@Bosswell, Jeśli użytkownik wpisze 400 stopni to będzie 400 stopni + k*360 stopni. Zgadza się?

komentarz 22 września 2021 przez Bosswell Nałogowiec (36,470 p.)
edycja 22 września 2021 przez Bosswell
Dla 400
(int) 400 / 180 = 2
400 - 2 * 180 = 40
--------
Dla 270
(int) 270 / 180 = 1
270 - 1 * 180 = 90
--------
Dla -90
(int) -90 / 180 = 0
-90 - 0 * 180 = -90
--------
Dla -450
(int) -450 / 180 = -2
-450 - (-2 * 180) = -450 + 360 = -90
--------
a = stopnie;
b = parseInt(a / 180);
c = Math.abs(a - b * 180);

if (c === 90) {
   console.log("Brak rozwiązań");
}

Prawdę mówiąc, to we wcześniejszej odpowiedzi popełniłem błąd z ujemnym tangensem.
O ujemnym tangensie możemy mówić, kiedy obliczamy granice. W tym przypadku jest to chyba niedopuszczalne.
komentarz 22 września 2021 przez Martita Bywalec (2,470 p.)
Czy to da mi wynik nieskończoność?
komentarz 22 września 2021 przez Bosswell Nałogowiec (36,470 p.)
Tak
komentarz 22 września 2021 przez Martita Bywalec (2,470 p.)

@Bosswell, 

Kod musi mieć document.getElementById i const.

Nie wiem gdzie w kodzie umieścić:

if (c === 90) {
   console.log("Brak rozwiązań");
}
Proszę o pomoc i sprawdzenie czy mam dobrze.

function myFunction() {
const SA = parseInt(document.getElementById("SA").value);
const TAN = Math.abs(parseInt(SA) - (parseInt(SA)  / 180) * 180);
    document.getElementById("TAN").innerHTML = Math.abs(SA - (SA / 180) * 180) + '=' + TAN.toFixed(4);
   
return false;
}

function resetuj () {
    document.getElementById("TAN").innerHTML = '';
}
komentarz 22 września 2021 przez Bosswell Nałogowiec (36,470 p.)
To console.log jest tylko przykładem. Zamiast niego możesz użyć document.getElementById("TAN").innerHTML = 'Pewien wynik'. Warunkowo pokaż wynik numeryczny albo też nieskończoność (dla c === 90 w tym przypadku). Myślę, że dałem kilka wskazówek. Nie chciałbym dawać gotowego kodu.
komentarz 24 września 2021 przez Martita Bywalec (2,470 p.)

@Bosswell, błagam o pomoc nie wiem jak to zrobić . Błagam popraw mój kod. Nie wiem gdzie mam źle. Dopiero się uczę. Poprawiłam kod ale nadal nie działa. Po wprowadzeniu 45 wynik pokazuje 0 a powinien 1. Natomiast po wprowadzeniu 90 wynik też pokazuje 0 zamiast nie istnieje. 

Warunkowo pokaż wynik numeryczny albo też nieskończoność (dla c === 90 w tym przypadku).  - Nierozumiem

function oblicz() {
const SA = parseInt(document.getElementById("SA").value);
const TAN = Math.abs(parseInt(SA) - (parseInt(SA)  / 180) * 180);
    document.getElementById("TAN").innerHTML = Math.abs(SA - (SA / 180) * 180) + '=' + TAN.toFixed(4);
    if(TAN==SA){
        document.getElementById("TAN").innerHTML = 'Nie istnieje';
    } 
       

}

function resetuj () {
    document.getElementById("TAN").innerHTML = '';
   
}

 

komentarz 24 września 2021 przez Bosswell Nałogowiec (36,470 p.)
function myFunction() {
  const container = document.getElementById("TAN");
  const deegres = parseInt(
    document.getElementById("SA").value
  );

  const normalizedDeegres = Math.abs(
    deegres - parseInt(deegres / 180) * 180
  );

  if (normalizedDeegres === 90){
      container.innerHTML = '<b>Nie istnieje! - &#8734</b>';

      return;
  }

  container.innerHTML = `<b>${Math.tan(deegres)}</b>`;
}

 

komentarz 25 września 2021 przez Martita Bywalec (2,470 p.)
Dzięki. Szczegółowo przeanalizowałam kod i teraz już wiem jak to się robi. Ja właśnie przy robieniu różnych zadań uczę się JavaScript.
komentarz 25 września 2021 przez Martita Bywalec (2,470 p.)

@Bosswell, Jak mogę oznaczyć że Twoja odpowiedź jest najlepsza?

komentarz 26 września 2021 przez Bosswell Nałogowiec (36,470 p.)
Dodałem nową odpowiedź z podsumowaniem
komentarz 26 września 2021 przez Martita Bywalec (2,470 p.)
Dzięki. Zaznaczyłam Twoją odpowiedź jako najlepszą.
0 głosów
odpowiedź 22 września 2021 przez Martita Bywalec (2,470 p.)
edycja 24 września 2021 przez Martita

POMÓŻCIE BŁAGAM

Po wprowadzeniu liczby 90 stopni pokazuje wynik Nie istnieje ale po wprowadzeniu 45 wynik  też pokazuje Nie istnieje zamiast 1.0000.

Nie wiem gdzie mam błąd. Błagam pomóżcie.

function myFunction() {
const SA = parseInt(document.getElementById("SA").value);
const TAN = Math.abs(parseInt(SA) - (parseInt(SA)  / 180) * 180);
   if (TAN === SA){
        document.getElementById("TAN").innerHTML = Math.abs(SA - (SA / 180) * 180) + '=' + ' <b>Nie istnieje! - &#8734</b> ' + TAN.toFixed(4);
    }
  
function resetuj () {
    document.getElementById("TAN").innerHTML = '';
}

 

komentarz 24 września 2021 przez Martita Bywalec (2,470 p.)
Błagam pomóżcie . Nie wiem jak to zrobić. Szukałam i nie znalazłam.

Podobne pytania

0 głosów
1 odpowiedź 163 wizyt
0 głosów
1 odpowiedź 246 wizyt
pytanie zadane 30 sierpnia 2022 w C i C++ przez benny13 Obywatel (1,150 p.)
0 głosów
1 odpowiedź 1,081 wizyt

92,568 zapytań

141,420 odpowiedzi

319,622 komentarzy

61,956 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!

...