Vinnaren i pepparkakshustävlingen!
2018-09-08, 16:37
  #1
Medlem
Ranndalls avatar
Kom inte ihåg exakta orsaken men visst är det extra svårt att jämföra två flyttal i C? Har ett program som just ska göra det. Problemet är att programmet alltid tror att flyttalen är lika stora. Postar här endast en del av hela koden. Tänker att det borde enkelt se hur jag tänker fel, behöver hela koden postas kan jag göra det.
Kod av funktion:
Citera
2018-09-08, 16:46
  #2
Medlem
Citat:
Ursprungligen postat av Ranndall
Kom inte ihåg exakta orsaken men visst är det extra svårt att jämföra två flyttal i C? Har ett program som just ska göra det. Problemet är att programmet alltid tror att flyttalen är lika stora. Postar här endast en del av hela koden. Tänker att det borde enkelt se hur jag tänker fel, behöver hela koden postas kan jag göra det.
Kod av funktion:

Du får ta en titt på det logiska villkoret som ser felaktigt ut. Du kanske hade tänkt använda '&&' istället för '||'.
Citera
2018-09-08, 16:58
  #3
Medlem
DigiFlaxs avatar
Mindre än 0.00001 eller större än -0.00001. Detta kommer alltid att vara sant.

Testa istället:
Kod:
if ((tal1 - tal2) < 0.00001f && (tal1 - tal2) > -0.00001f)
Citera
2018-09-08, 17:02
  #4
Medlem
Inte svar på frågan men ett tips, om du tar absolutvärdet av skilnaden så slipper du ha eller.

Detta funkar för mig vid en snabb test:

Kod:
#include <stdio.h>
#include <math.h>

int flyttal(float tal1, float tal2)
{
  if (fabs(tal1 - tal2) < 0.00001)
    return 1;
  else
    return 0;
}

int main() {
  if (flyttal(0.123456789, 0.1234567890))
    printf("1");
  else
    printf("0");
}
__________________
Senast redigerad av LoveShy 2018-09-08 kl. 17:11.
Citera
2018-09-08, 17:13
  #5
Medlem
Ranndalls avatar
Citat:
Ursprungligen postat av alamos
Du får ta en titt på det logiska villkoret som ser felaktigt ut. Du kanske hade tänkt använda '&&' istället för '||'.
Tackar det var det plus ett annat fel i övriga koden.
Citat:
Ursprungligen postat av DigiFlax
Mindre än 0.00001 eller större än -0.00001. Detta kommer alltid att vara sant.

Testa istället:
Kod:
if ((tal1 - tal2) < 0.00001f && (tal1 - tal2) > -0.00001f)
Nu löste jag det nog med att byta ut till &&. Men av nyfiken undrar jag det betyder när du skriver f på det där sättet?
__________________
Senast redigerad av Ranndall 2018-09-08 kl. 17:17.
Citera
2018-09-08, 18:28
  #6
Medlem
Citat:
Ursprungligen postat av Ranndall
Tackar det var det plus ett annat fel i övriga koden.

Nu löste jag det nog med att byta ut till &&. Men av nyfiken undrar jag det betyder när du skriver f på det där sättet?

0.1 är representerat som en double medans 0.1f är representerat som en float.

Kod:
//Test 1
    
float f 0.1;
    
bool b == 0.1 true false;
    
printf("%d\n",b); //b blir falskt eftersom du testar ifall f är en double, fast det är en float. Det blir skillnad på avrundningen.
 
    //Test 2
    
float f 0.1;
    
bool b == 0.1f true false;
    
printf("%d\n",b); //Här blir b sant eftersom du testar om din float f är en float. 


Man skriver "f" efter en siffra för att vara övertydlig med att det är just en float vi ska syssla med och inte någon annan datatyp.
Citera
2018-09-09, 20:54
  #7
Medlem
Citat:
Ursprungligen postat av Ranndall
Men av nyfiken undrar jag det betyder när du skriver f på det där sättet?
0.1f är float, 0.1 är double, 0.1L är long double.
Citera
2018-09-09, 22:58
  #8
Moderator
Neksnors avatar
Är kass på C, men kan man inte jämföra hur de representeras, alltså bitmönster? Samma mönster -> samma tal. Fast relationer som < och > blir knepiga.
Citera
2018-09-10, 05:00
  #9
Medlem
Citat:
Ursprungligen postat av Neksnor
Är kass på C, men kan man inte jämföra hur de representeras, alltså bitmönster? Samma mönster -> samma tal. Fast relationer som < och > blir knepiga.

Det hjälper inte, snarare tvärtom. IEEE 754 tillåter (för enkelhets/hastighets skull antar jag) bl.a. att talet noll har signbiten antingen av eller på, och har alltså en positiv nolla och en negativ. Den senare kan t.ex. uppstå om man multiplicerar ett negativt tal som -1.0 med 0.0. Så bitmönstren kan vara olika, men jämförs de med operatorer som == kräver standarden att de ska behandlas som lika.

Kod:
#include <stdio.h>
#include <string.h>

static const char *eqStr = "lika", *neStr = "olika";

int main(void) {
    double neg, pos;

    pos = 0.0;
    neg = -0.0;
    (void) printf("%s bitmönster\n", (memcmp(&pos, &neg, sizeof pos) == 0) ? eqStr : neStr);
    (void) printf("%s enligt ==\n", (pos == neg) ? eqStr : neStr);
    return 0;
}

Ger på min dator:

Kod:
olika bitmönster
lika enligt ==
Citera
2018-09-10, 11:57
  #10
Medlem
Det borde inte vara svårt att fixa bitvis (sen om det är bästa sättet är en annan sak). Kolla om alla bitar utom sign är 0, isåfall sätt sign till 0, sen kör som vanligt.
Citera
2018-09-10, 15:35
  #11
Medlem
Citat:
Ursprungligen postat av LoveShy
Det borde inte vara svårt att fixa bitvis (sen om det är bästa sättet är en annan sak). Kolla om alla bitar utom sign är 0, isåfall sätt sign till 0, sen kör som vanligt.

Förutsatt att systemet verkligen använder IEEE 754, vilket är det absolut vanligaste, skulle man kunna göra så här och slippa veta eller ta reda på endianness för att hitta signed-biten:

Kod:
double n;
...
/* normalisera noll */
if (n == 0.0) memset(&n, 0, sizeof n);

Men det löser som sagt inte problemet med att flyttal har begränsad upplösning. https://stackoverflow.com/a/2100502 verkar förklara det ganska bra, och ett tillägg är att standarden dessutom godkänner fem olika avrundningsregler för att fungera med alla processorer. Alla avrundar till något av de två värden som är närmast det exakta, men skiljer sig åt i hur de gör det. Tänk dig en decimal flyttalstyp med 8 siffor, där 1/3 kan bli antingen 0.33333333 eller 0.33333334.

Heltal är inga problem med +, -, * och /, så länge man inte överflödar. 81.0 / 9.0 är alltid 9.0. Men då kan man ju lika gärna använda en heltalstyp. Andra operationer, som innefattar algoritmer och oändlighet, ska man nog inte lita alltför mycket på, åtminstone om man ska tro https://stackoverflow.com/a/26071783/1246115.
Citera

Stöd Flashback

Flashback finansieras genom donationer från våra medlemmar och besökare. Det är med hjälp av dig vi kan fortsätta erbjuda en fri samhällsdebatt. Tack för ditt stöd!

Stöd Flashback