b) -1
*a = 1, b = 2, c = 3 *a = 2, b = 3, c = 5
Koden för inläsningen är med två gånger, så den här lösningen bryter mot DRY-principen ("Don't Repeat Yourself"). Vi kan försöka få bort den upprepningen:#include <stdio.h> int main(void) { int a, b, c; printf("Skriv tre heltal. Avsluta med 0 0 0: \n"); scanf("%d %d %d", &a, &b, &c); while (a != 0 || b != 0 || c != 0) { printf("Medelvärde: %f\n", (a + b + c) / 3.0); printf("Skriv tre heltal. Avsluta med 0 0 0: \n"); scanf("%d %d %d", &a, &b, &c); } return 0; }
Men där behövde vi i stället upprepa villkoret att minst ett tal måste vara skilt från noll. Vi kan slippa upprepningen genom att placera inläsning och kontroll i en egen funktion:#include <stdio.h> int main(void) { int a, b, c; a = b = c = 1; // Så alla inte är 0 while (a != 0 || b != 0 || c != 0) { printf("Skriv tre heltal. Avsluta med 0 0 0: \n"); scanf("%d %d %d", &a, &b, &c); if (a != 0 || b != 0 || c != 0) printf("Medelvärde: %f\n", (a + b + c) / 3.0); } return 0; }
Nu bryter vi inte mot DRY-principen. Men kanske bryter vi i stället mot KISS-principen ("Keep it simple, stupid")? Övning: Diskutera vilken lösning som är bäst!#include <stdio.h> int las(int *ap, int* bp, int *cp) { printf("Skriv tre heltal. Avsluta med 0 0 0: \n"); scanf("%d %d %d",ap, bp, cp); return *ap != 0 || *bp != 0 || *cp != 0; } int main(void) { int a, b, c; while (las(&a, &b, &c)) { printf("Medelvärde: %f\n", (a + b + c) / 3.0); } return 0; }
struct Triangel t = { 3, 4, 3.5 };
void visa_triangel(struct Triangel *tp) { printf("Sida a = %f, b = %f, c = %f\n", tp->a, tp->b, tp->c); }
void las_triangel(struct Triangel* tp) { printf("Ange sida a: "); scanf("%lf", &tp->a); printf("Ange sida b: "); scanf("%lf", &tp->b); printf("Ange sida c: "); scanf("%lf", &tp->c); // Notera att nu finns det troligen kvar ett radslutstecken i inmatningsbufferten }
double min(struct Triangel t) { if (t.a <= t.b && t.a <= t.c) return t.a; else if (t.b <= t.c) return t.b; else return t.c; } double max(struct Triangel t) { if (t.a >= t.b && t.a >= t.c) return t.a; else if (t.b >= t.c) return t.b; else return t.c; }
Eller, om man har tråkigt:
int compare_doubles(const void *p1, const void *p2) { double d1 = *((double*)p1); double d2 = *((double*)p2); if (d1 < d2) return -1; else if (d1 == d2) return 0; else /* d1 > d2 */ return 1; } double *sort_sides(struct Triangel t) { static double a[3]; a[0] = t.a; a[1] = t.b; a[2] = t.c; qsort(a, 3, sizeof(a[0]), compare_doubles); return a; } double min(struct Triangel t) { return sort_sides(t)[0]; } double max(struct Triangel t) { return sort_sides(t)[2]; }
void skala(struct Triangel* tp, double faktor) { tp->a *= faktor; tp->b *= faktor; tp->c *= faktor; }
double area(struct Triangel* tp) { double a = tp->a; double b = tp->b; double c = tp->c; return a/2 * sqrt( b*b - pow( (a*a + b*b - c*c) / (2*a), 2 ) ); }
int main(void) { struct Triangel triangeln; double skalfaktor; las_triangel(&triangeln); visa_triangel(&triangeln); printf("Arean = %f\n", area(&triangeln)); printf("Ange en skalfaktor: "); scanf("%lf", &skalfaktor); skala(&triangeln, skalfaktor); visa_triangel(&triangeln); printf("Arean = %f\n", area(&triangeln)); return 0; }
double max_area(struct Triangel trianglar[], int antal_trianglar) { double max_area_so_far = area(&trianglar[0]); for (int i = 1; i < antal_trianglar; ++i) { double this_area = area(&trianglar[i]); if (this_area > max_area_so_far) { max_area_so_far = this_area; } } return max_area_so_far; }
Tänk på att beräkningar med flyttal inte alltid ger exakta svar. Nu borde funktionen area ge samma svar för triangeln a[2] varje gång man anropar den, men egentligen kan man inte vara säker ens på det.int main(void) { struct Triangel a[] = { { 3, 4, 5 }, { 4, 5, 3 }, { 5, 3, 4 }, { 3, 4, 4 }, }; if (max_area(a, 4) != area(&a[2])) printf("*** FEL svar från funktionen max_aree!\n"); return 0; }
#include <stdlib.h> #include <stdio.h> // ... int main(void) { struct Triangel triangel; printf("Mata in en triangel.\n"); las_triangel(&triangel); FILE *tsut = fopen("triangel.txt", "w"); if (tsut == NULL) { fprintf(stderr, "Kunde inte skriva filen 'triangel.txt'.\n"); exit(EXIT_FAILURE); } fprintf(tsut, "%f %f %f", triangel.a, triangel.b, triangel.c); fclose(tsut); printf("Nu är triangeln sparad på filen 'triangel.txt'.\n"); return EXIT_SUCCESS; }
En kommentar om felhantering: Egentligen kan både fprintf och fclose misslyckas, så i ett riktigt program bör man kontrollera returvärdet från dessa funktionsanrop.
#include <stdlib.h> #include <stdio.h> // ... int main(void) { struct Triangel triangel; FILE *tsin = fopen("triangel.txt", "r"); fscanf(tsin, "%lf %lf %lf", &triangel.a, &triangel.b, &triangel.c); fclose(tsin); printf("Triangeln som är sparad på filen 'triangel.txt':\n"); visa_triangel(&triangel); return EXIT_SUCCESS; }
Men utskriften kan också bli så här:1.200000 1.200000 Va? Inte lika!
1.200000 1.200000 Lika, förstås.
Datorns flyttal lagras med binära siffor, även kallade bitar, men ett begränsat antal. Därför kan inte alla tal lagras exakt. Jämför med talet 1/3, som inte kan skrivas exakt med ändligt många vanliga decimala siffror (0-9). Det blir 0.3333333333... med treor i all oändlighet.
Om man vill förenkla lite kan man tänka sig att alla flyttal kan få ett litet, slumpmässigt fel mot slutet av decimalerna. (På riktigt är det inte slumpmässigt, och inte alla tal, men man kan tänka så.) 0.2 lagras kanske inte som exakt 0.2, utan som 0.200000000000000011102230246251565404236316680908203125. Därför måste man vara försiktig med exakta jämförelser av flyttal. 0.2 * 6 blir inte nödvändigtvis exakt lika med 1.2.
Prova själv att ändra utskrifterna i programmet så talen skrivs ut med 100 decimaler:
printf("%.100f\n", x * y); printf("%.100f\n", z);
Tänk också på att även ett tal som går att skriva exakt med decimala siffror, som till exempel 0.2 och 1.2, inte nödvändigtvis kan lagras exakt med binära siffror.
Det gäller både float och double. double lagras med fler siffror än float, men det är fortfarande ändligt många.
Läs mer i David Goldbergs artikel What Every Computer Scientist Should Know About Floating-Point Arithmetic. Den finns till exempel här: http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html