Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: C++: Desimaalilukujen summan tarkastus ei toimi

Sellasta [20.02.2017 22:04:13]

#

Hei! Olen melko harrastelijatasoinen, ja olen kokeillut monia korjauksia koodiin, joten anteeksi, jos kirjoittamani koodi on hieman outoa, tyhmää (korjauksista jääneitä outouksia) tai yksinkertaista.

Olisi ongelma. Yritin tehdä yksinkertaisen ohjelman, joka arpoo "desimaalilukuja", ja käyttäjä laskee ne yhteen. 2 desimaalia. Ohjelma näyttää toimivan usein ihan hyvin, mutta sitten tuleekin tenkkapoo, ja ohjelma väittää, että annettu vastaus on väärä, vaikka ohjelma itse tulostaa vielä saman lukeman varmistukseksi. Omat taidot eivät nyt riitä käsittämään. Onko ongelma ehkä jokin tuolla desimaalijutussa.

Ja kiersin desimaaliluvun luomisen luomalla ensin "pääluvun", ja siihen summaan "kontrolloidun" desimaaliosan. Näin koska, en osannut rajoittaa desimaalin luontia 2 desimaaliin ilman pyöristys yms ongelmia.

#include <iostream>
#include <stdlib.h>
#include <cstdlib>
#include <limits>

using namespace std;

double Numero();

int main()
{
srand(time(0));

do{

int lkm = 0;
double arvo = 0;
double vastaus = 0.0;
double summa = 0.0;

cout << "Montako numeroa?: ";
cin >> lkm;
cout << endl;

//numeroiden tulostus ja summaus
for(int i = 0; i < lkm; i++)
{
    arvo = 0;
    arvo = Numero();
    cout << arvo << endl;
    summa = summa+arvo;
}

cout << "\nAnna summa: ";
cin >> ws >> vastaus;

//vastauksen tarkistus
if (vastaus != summa)
{
    cout << "Väärin..." << endl;
    cout << "Oikea summa oli: " << summa << endl;
}
else
{
    cout << "OIKEIN!" << endl;
}

cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');  //tällä tarvitaan vain 1 kpl cin.get() pysäytykseen
cin.get();

system("clear");

}while(1);

return 0;
}

//aliohjelma arpoo numerot. arvotaan pääluku, ja siihen ynnätään luotu desimaaliluku
double Numero(){

double numero1 = 0; //pääluku
double numero2 = 0; //pääluku+desimaali

double desi1 = 0; //desimaalin pääluku
double desi2 = 0; //josta tehdään desimaali jakamalla

desi1 = (rand()%99);
desi2 = desi1/100;

numero1 = (rand()%10);
numero2 = numero1 + desi2;

return numero2;
}

Esimerkkinä:
Anna summa: 10.88
Väärin...
Oikea summa oli: 10.88

Annetun summan pitäisi olla yhtä suuri ohjelman laskeman summan kanssa, mutta ohjelma on eri mieltä. Olen varmaan vain tehnyt jotain yksinkertaisen tyhmää, joka menee ohi silmien? Ensiksi luulin, että ohjelma hajoaa enter-painalluksesta, siksi siellä on tuo limits juttu. Ei ollut siinä kai ongelma. Epäilin, että sinne kuitenkin tulisi jokin "whitespace" vai miksikä kutsutaankaan, mutta ei se kai ollut ongelma. Tietenkään syötön virheentarkastusta en ole tuohon jaksanut (yrittää) tehdä, kun vain itse käyttää. Vika on varmaan tuo desimaalikyhäelmä, en vain osaa paremmin tehdä.

Lukemia, joilla väärin-ilmoitus tullut:

6.31
2
=====
8.31

0.05
3.61
=====
3.66

3.31
8.55
=====
11.86

9.84
7.65
=====
17.49

Grez [21.02.2017 12:09:52]

#

Perusongelma on se, että double ei ole tarkka (eli kaikkia lukuja ei pysty esittämään doublena ja siksi tulee pyöristysvirheitä), ja sen takia yleisesti ottaen koodissa ei koskaan pitäisi vertailla kahta doublea keskenään.

Eli käytännössä virhe on kohdassa vastaus != summa

Jos luvut ei pyöristyisi tulostuksessa, näkisit ongelman havainnollisemmin...

Esim. Anna summa: 10.88
Väärin...
Antamasi summa: 10.8800000000000000001
Oikea summa: 10.8799999999999999999

Yksi ratkaisu olisi käyttää ohjelman sisäisesti kokonaislukuja ja vaan näyttää / lukea desimaalipilkku siirrettynä.

TapaniS [21.02.2017 12:14:05]

#

Löytyi pari sivua, joissa sama asia on tuottanut päänvaivaa:

Linkki 1
Linkki 2

Näissä todetaan, että vertailu voi tuottaa epätoivottuja tuloksia ja annetaan ratkaisuehdotus, että määritetään pieni luku, epsilon, jota käytetään vertailussa (Luku1 - Luku2) < epsilon.

Sellasta [21.02.2017 15:53:37]

#

Kiitän suuresti vastauksistanne, tarkastelen asiaa näiden valossa, katselen, josko onnistun saamaan toimimaan! :)

Grez [21.02.2017 21:07:27]

#

TapaniS kirjoitti:

epsilon, jota käytetään vertailussa (Luku1 - Luku2) < epsilon.

Tuo ei suoraan toimi. Pitäisi olla

abs(luku1 - luku2) < epsilon

tai

(luku1 - luku2) < epsilon && (luku2 - luku1) < epsilon

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta