Διαίρεση δεκαδικών

Giorgos Keramidas keramida at ceid.upatras.gr
Sun May 12 00:53:01 EEST 2002


On 2002-05-11 20:20, drcypher wrote:
> Δε θυμάμαι αν το θέμα έχει ξανατεθεί...

Ούτε εγώ θυμάμαι.  Αλλαξα το στυλ του κώδικα για να διαβάζεται ολίγον
τι καλύτερα.  Η αρίθμηση έγινε με το nl(1):

     1	#include <stdio.h>

     2	int
     3	main(void)
     4	{
     5		double a, b;

     6		a = 10.0;
     7		b = 1.0;

     8		printf("(%g/%g == 10.0) == %s\n", a, b,
     9		    (a/b == 10.0) ? "true" : "false");

    10		printf("(%g/%g == 10.0) == %s\n", 10.0, 1.0,
    11		    (10.0/1.0 == 10.0) ? "true" : "false");

    12		a = 1.0;
    13		b = 0.1;

    14		printf("(%g/%g == 10.0) == %s\n", a, b,
    15		    (a/b == 10.0) ? "true" : "false");

    16		printf("(%g/%g == 10.0) == %s\n", 1.0, 0.1,
    17		    (1.0/0.1 == 10.0) ? "true" : "false");

    18		return 0;
    19	}

Σε όλα τα printf (γραμμές 8-9, 10-11, 14-15 και 16-17) κάνεις σύγκριση
floating point αριθμών με ==, κάτι που δεν είναι σωστό.  Δεν μπορείς
να συγκρίνεις αριθμούς float/double με == γιατί είναι σχεδόν εγγυημένο
ότι δεν θα σου βγάλει πάντα σωστά αποτελέσματα.  Δες τι βγάζει το
παρακάτω πρόγραμμα:

     1	#include <stdio.h>

     2	int
     3	main(void)
     4	{
     5		printf("%.70lf\n", ((double)1.0 / 1.000000001));
     6		printf("%.70lf\n", ((double)1.000000001 / 1.0));
     7		printf("%.70lf\n", ((double)0.0075 / 0.0025));
     8		printf("%.70lf\n", ((double)0.075 / 0.025));
     9		printf("%.70lf\n", ((double)0.75 / 0.25));
    10		printf("%.70lf\n", ((double)7.5 / 2.5));
    11		printf("%.70lf\n", ((double)75.0 / 25.0));

    12		return 0;
    13	}

Όταν το τρέξεις τα αποτελέσματα είναι αρκετά ενδιαφέροντα πιστεύω:

	hades+charon:/tmp$ cc -o foo foo.c
	hades+charon:/tmp$ ./foo
	0.9999999989999999172596290009096264839172363281250000000000000000000000
	1.0000000010000000827403709990903735160827636718750000000000000000000000
	3.0000000000000000000000000000000000000000000000000000000000000000000000
	2.9999999999999995559107901499373838305473327636718750000000000000000000
	3.0000000000000000000000000000000000000000000000000000000000000000000000
	3.0000000000000000000000000000000000000000000000000000000000000000000000
	3.0000000000000000000000000000000000000000000000000000000000000000000000

Και η ερώτηση παγίδα.  Για την συγκεκριμένη εφαρμογή που κάνεις, το
τέταρτο νούμερο (αυτό που τυπώνει η γραμμή 8) που είναι απειροελάχιστα
κάτω από το 3.000 είναι "αρκετά καλή τιμή" για να θεωρηθεί ίσο με
3.000;  Γιατί αν είναι αρκετά καλή τιμή, το == δεν θα το πιάσει όπως
δείχνει το παρακάτω πρόγραμμα:

     1	#include <stdio.h>

     2	int
     3	main(void)
     4	{
     5		double a = 0.0075 / 0.0025;
     6		double b = 0.075 / 0.025;

     7		printf("%s\n", (a == b) ? "TRUE" : "FALSE");

     8		return 0;
     9	}

	hades+charon:/tmp$ cc -o bar bar.c
	hades+charon:/tmp$ ./bar
	FALSE

Η διαφορά όμως είναι τόσο μικρή που πρακτικά για τις πιο πολλές
εφαρμογές είναι αμελητέα.  Το από ποιά διαφορά και κάτω είναι
πραγματικά αδιάφορο στο πρόγραμμα σου, μπορείς να το ορίσεις με κάτι
σαν το εξής:

	#define EPSILON		((double)1.0e-15)

και μετά να συγκρίνεις τις διαφορές των τιμών που βρίσκεις για να δεις
αν απολύτως είναι μικρότερες του EPSILON με την fabs():

	#include <math.h>	/* For fabs() */

	if (fabs(val1 - val2) < EPSILON) {
		true;
	} else {
		false;
	}

Και μην ακούσω κανέναν και λέει ότι η C δεν είναι καλή για μαθηματικά
γιατί θα τον βάλω για άσκηση να κάνει σε C πρόγραμμα που τυπώνει την
τιμή των δύο double που έχει το bar.c αλλά όχι με %lf.  Bit προς bit,
και τη μία τιμή κάτω από την άλλη, για να μας αποδείξει ότι δεν είναι
λάθος της C το παραπάνω αλλά της floating point υλοποίησης που έχει ο
επεξεργαστής (που είναι IEEE πρότυπο, παρεπιπτόντως).

Αρκετά σας έπρηξα πάλι όμως, χαίρετε.

- Giorgos




More information about the Linux-greek-users mailing list