Исключения при работе с числами с плавающей запятой

Допустим, вы решили обрабатывать у себя в программе исключения, которые могут генерироваться при операциях с плавающей запятой. Это можно сделать в обычном C коде и механизм исключений из С++ не нужен. В данной заметке я приведу код для воспроизведения пяти ошибок и их обработки в ОС linux.
#define PRINTF (void)printf
 
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
 
#pragma STDC FENV_ACCESS ON
#include <fenv.h>
 
int		main (void);
static void	handle_exc(int mask);
int		add_floats(float a, float b, float* result);
 
int main()
{
    double inf;
    float zero, one;
    float result;
 
    zero = 0.0;
    inf = 1.0;
    inf = inf/zero;
 
    int fpeRaised; // we store flags in here
 
    /*----------------------------------------------------------------*/
    feclearexcept(FE_ALL_EXCEPT);
 
    PRINTF("\n\nBegin test: result = inf*zero\n");
    result = inf*zero;
 
    fpeRaised = fetestexcept(FE_ALL_EXCEPT);
 
    //PRINTF("%d\n", fpeRaised);
    handle_exc(fpeRaised);
    PRINTF("\nInf = %g\tZero = %g\tInf*Zero = %g\n",
		 inf, zero, result);
 
    /*----------------------------------------------------------------*/
    feclearexcept(FE_ALL_EXCEPT);
    PRINTF("\n\nBegin test: result = one + inf*zero\n");
 
    one = 1.0;
    result = one + inf*zero;
 
    fpeRaised = fetestexcept(FE_ALL_EXCEPT);
    //PRINTF("%d\n", fpeRaised);
    handle_exc(fpeRaised);
    PRINTF("\nOne = %g\tInf = %g\tZero = %g\tOne + Inf*Zero = %g\n",
		 one, inf, zero, result);
    /*----------------------------------------------------------------*/
    feclearexcept(FE_ALL_EXCEPT);
    PRINTF("\n\nBegin test: result = inf + inf\n");
 
    inf = one/zero;
    result = inf;
    feclearexcept(FE_ALL_EXCEPT);
    result = inf + inf;
 
    fpeRaised = fetestexcept(FE_ALL_EXCEPT);
    //PRINTF("%d\n", fpeRaised);
    handle_exc(fpeRaised);
    PRINTF("\nInf = %g\tInf = %g\tInf + Inf = %g\n",
		 inf, inf, result);
 
    /*----------------------------------------------------------------*/
    feclearexcept(FE_ALL_EXCEPT);
    /* expect no exceptions here */
    PRINTF("\n\nBegin test: result = 1. + 2.\n");
    one = 1.0;
    float two = 2.2;
    result = one + two;
 
    fpeRaised = fetestexcept(FE_ALL_EXCEPT);
    //PRINTF("%d\n", fpeRaised);
    handle_exc(fpeRaised);
    PRINTF("\nOne = %g\tTwo = %g\tOne + Two = %g\n",
		 one, two, result);
 
    /*----------------------------------------------------------------*/
    feclearexcept(FE_ALL_EXCEPT);
    PRINTF("\n\nBegin test: result = 0. / 0.\n");
 
    result = zero/zero;
 
    fpeRaised = fetestexcept(FE_ALL_EXCEPT);
    //PRINTF("%d\n", fpeRaised);
    handle_exc(fpeRaised);
 
    PRINTF("\nZero = %g\tZero = %g\tZero/Zero = %g\n",
		 zero, zero, result);
 
    /*----------------------------------------------------------------*/
    feclearexcept(FE_ALL_EXCEPT);
    PRINTF("\n\nBegin test: underflow\n");
 
    double a = 1e-40;
    result = a;
 
    fpeRaised = fetestexcept(FE_ALL_EXCEPT);
    //PRINTF("%d\n", fpeRaised);
    handle_exc(fpeRaised);
 
    PRINTF("\nResult = %g\n",
		 result);
 
    /*----------------------------------------------------------------*/
    feclearexcept(FE_ALL_EXCEPT);
    PRINTF("\n\nBegin test: sqrt for a negative number\n");
 
    result = sqrt( -1.00 );
 
    fpeRaised = fetestexcept(FE_ALL_EXCEPT);
    //PRINTF("%d\n", fpeRaised);
    handle_exc(fpeRaised);
 
    PRINTF("\nsqrt for a negative value %g\n",
		 result);
 
    /*----------------------------------------------------------------*/
    feclearexcept(FE_ALL_EXCEPT);
    PRINTF("\n\nBegin test: result = 1. + HUGE\n");
 
    one = 1.0;
    float huge = 1.0E100;
    result = one + huge;
 
    fpeRaised = fetestexcept(FE_ALL_EXCEPT);
    //PRINTF("%d\n", fpeRaised);
    handle_exc(fpeRaised);
 
    PRINTF("\nOne = %g\tHuge = %g\tOne + Huge = %g\n",
		 one, huge, result);
 
 
    /*----------------------------------------------------------------*/
    /* Divide-by-zero operation */
    feclearexcept(FE_ALL_EXCEPT);
    PRINTF("\n\nBegin test: division by zero\n");
    result = one / zero;
 
    fpeRaised = fetestexcept(FE_ALL_EXCEPT);
    //PRINTF("%d\n", fpeRaised);
    handle_exc(fpeRaised);
 
    PRINTF("\ndivision by zero %g\n",
		 result);
    /*----------------------------------------------------------------*/
    /* overflow */
    feclearexcept(FE_ALL_EXCEPT);
    PRINTF("\n\nBegin test: overflow\n");
    result = expf(88.8);
 
    fpeRaised = fetestexcept(FE_ALL_EXCEPT);
    //PRINTF("%d\n", fpeRaised);
    handle_exc(fpeRaised);
 
    PRINTF("\nOverflow %g\n",
		 result);
    /*----------------------------------------------------------------*/
    /*testing inexact*/
    feclearexcept(FE_ALL_EXCEPT);
    PRINTF("\n\nBegin test: inexact\n");
 
    double c = 0.1;
    c = sin(30) * a;
 
    fpeRaised = fetestexcept(FE_ALL_EXCEPT);
    //PRINTF("%d\n", fpeRaised);
    handle_exc(fpeRaised);
 
    PRINTF("\nInexact, %g \n", c);
    /*----------------------------------------------------------------*/
    PRINTF("\n\nBegin test: add function\n");
    fpeRaised = add_floats(3., one, &result);
    handle_exc(fpeRaised);
    PRINTF("\ntesting add function, %g \n", result);
 
    PRINTF("\n\nEnd tests\n");
 
    return (EXIT_SUCCESS);
}
 
int add_floats(float a, float b, float* result){
    feclearexcept(FE_ALL_EXCEPT);
    *result = a + b;
    int fpeRaised = fetestexcept(FE_ALL_EXCEPT);
    return fpeRaised;
}
 
static void handle_exc(int mask){
	if (mask & FE_INVALID)            PRINTF(" FP_INVALID_OPERATION ");
	if (mask & FE_DIVBYZERO)          PRINTF(" FP_ZERO_DIVIDE ");
	if (mask & FE_OVERFLOW)           PRINTF(" FP_OVERFLOW ");
	if (mask & FE_UNDERFLOW)          PRINTF(" FP_UNDERFLOW ");
	if (mask & FE_INEXACT)            PRINTF(" FP_INEXACT ");
}
[sc:social_networks ]
You can leave a response, or trackback from your own site.

Leave a Reply