#include <stdio.h>
#include <signal.h>
#include <setjmp.h>
#include <stdlib.h>
#include <float.h>
#include <math.h>
#include <time.h>
#define _MCW_EM 0x0000001 /* Interrupt-Maske */

/* Schnittstelle zur Assemblerroutine */
void fpadd(long double, long double, long double*);
int fpadd_v;

/* Hilfsfunktion zum Ausdrucken von Mantisse, Exponent und Vorzeichen */
static void printfp(long double*);

/* Abfangen eines Overflow-Fehlers bei der Addition durch den Compiler: */
jmp_buf mark; /* Adresse, an die der long jump springt */
void __cdecl fphandler(int, int); /* Handler */

/* Beim Zeitvergleich wird die Addition vom C-Compiler und von der Assembler-
   Routine jeweils 'waitticks' mal durchgeführt (muß für schnelle Maschinen
   erhöht werden). */
const long int waitticks = 100000;

int main() {
  long double a, b, c;
  long int i;
  int exp;
  clock_t ticks; /* int-Variable für die Zeitmessung */
  int jmpret;

  _control87(0, _MCW_EM); /* FP-Exceptions demaskieren */
  /* FP-Fehlerhandler einrichten - gibt eine Compilerwarnung, weil nur ein
     Argument erwartet wird */
  if (signal(SIGFPE, fphandler) == SIG_ERR) {
    fprintf(stderr, "SIGFPE konnte nicht eingerichtet werden.\n");
    abort();
  }

  do {
    /* Die Eingabe von sehr großen FP-Zahlen wird von den Eingabe-Routinen
       des Compilers nicht unterstützt, deswegen kann für jeden Summanden
       noch ein Exponent zur Basis 2 angegeben werden.
       Ein Overflow wird z.B. bei den Werten 1, 16383 für beide Summanden
       erzeugt. */
    a = 0; b = 0;
    printf("\n Ersten Summand und Exponent zur Basis 2 eingeben: ");
    scanf("%Lf, %d", &a, &exp);
    *(4+(unsigned *)&a) += exp;
    printf("Zweiten Summand und Exponent zur Basis 2 eingeben: ");
    scanf("%Lf, %d", &b, &exp);
    *(4+(unsigned *)&b) += exp;

    printfp(&a);
    printfp(&b);

    jmpret = setjmp(mark); /* Falls bei 'c = a + b' Overflow stattfindet */
    if (jmpret == 0) {
      c = a + b;
      printf("\nErgebnis des Compilers:        %Lf, Zeit: ", c);
      ticks = clock();
      for (i = 0; i < waitticks; i++) c = a + b;
      printf("%ld\n", clock() - ticks);
      printfp(&c);
    }
    else {
      printf("\nErgebnis des Compilers:        Overflow-Fehler\n");
    }

    fpadd_v = 0;
    fpadd(a,b,&c); /* setzt fpadd_v = -1, falls Overflow */
    if (!fpadd_v) {
      printf("\nErgebnis der Assemblerroutine: %Lf, Zeit: ", c);
      ticks = clock();
      for (i = 0; i < waitticks; i++) fpadd(a,b,&c);
      printf("%ld\n", clock() - ticks);
      printfp(&c);
    }
    else {
      printf("\nErgebnis der Assemblerroutine: Overflow-Fehler\n");
    }

  } while (a != 0.0 || b != 0.0);
  return 0;
}


/* Testfunktion; gibt für einen Zeiger auf ein long double die Werte
   von Exponent, Vorzeichen und Mantisse aus */
void printfp(long double* d) {
  char sign;
  short unsigned* c = (short unsigned *) d;

  if (*(c+4) & 0x8000) sign = '-'; else sign = '+';
  printf("Exponent = %d, Vorzeichen=(%c), ",
    *(c+4) & 0x7fff, sign);
  printf("Mantisse = %04X %04X %04X %04X\n",
    *(c+3), *(c+2), *(c+1), *c);
  return;
}


/* Handler für FP-Exceptions (hier nur Overflow möglich) */
void fphandler(int sig, int num) {
  _fpreset(); /* FP-System neu initialisieren */
  /* Umgebung wiederherstellen und zurück nach setjmp springen. Ergebnis-
     wert -1, damit abgefragt werden kann, ob ein Fehler stattgefunden hat */
  longjmp(mark, -1);
}
