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

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

Существует семь различных кодов исключений, которые могут возникать при выполнении операций с использованием данных вещественного типа. Первоначально эти исключения отключены и не могут возникать до тех пор, пока с помощью функции _controlfp для них не будет предварительно задана специальная маска, не зависящая от типа процессора. Предусмотрены отдельные исключения для ситуаций антипереполнения, переполнения, деления на ноль, неточного результата и так далее, что иллюстрируется приведенным ниже фрагментом кода. Для активизации исключений определенного типа следует отключить соответствующий бит маски. 

DWORD _controlfp(DWORD new, DWORD mask) 

Фактическое значение маски определяется ее текущим значением (current_mask) и двумя аргументами следующим образом:

(current_mask & ~mask) | (new & mask)

Данная функция устанавливает лишь те из битов, указанных в аргументе new, которые разрешены аргументом mask. Биты, не активизированные аргументом mask, не изменяются. Маска FP-исключений управляет также точностью, округлением и обработкой значений, соответствующих бесконечности, поэтому при активизации перечисленных исключений необходимо тщательно следить за тем, чтобы случайно не изменить эти установки.

Возвращаемым значением является фактическое значение маски. Так, при нулевых значениях обоих аргументов возвращаемым значением будет текущее значение маски (current_mask), что может быть использовано для восстановления маски, если впоследствии в этом возникнет необходимость. С другой стороны, если задать аргумент mask равным 0xFFFFFFFF, то регистр установится в new, что, например, может быть использовано для восстановления прежнего значения маски.

Обычно для того, чтобы разрешить исключения, связанные с выполнением операций над числами с плавающей точкой, в качестве аргумента mask используют константу MCW_EM, как продемонстрировано в следующем примере. Также заметьте, что при обработке FP-исключения оно должно быть сброшено путем использования функции _clearfp.

#include <float.h>

DWORD FPOld, FPNew; /* Старое и новое значения маски. */

FPOld = _controlfp(0, 0); /* Сохранить старую маску. */

/* Указать в качестве разрешенных шесть типов исключений. */

FPNew = FPOld & ~(EM_OVERFLOW | EM_UNDERFLOW | EM_INEXACT | EM_ZERODIVIDE | EM_DENORMAL | EM_INVALID);

/* Установить новую управляющую маску. Параметр MCW_EM объединяет шесть исключений, указанных в предыдущем операторе. */

_controlfp(FPNew, MCW_EM);

while(…) __try { /* Выполнить вычисления над числами с плавающей точкой. */

 … /* На этом участке кода может возникнуть FP-исключение. */

} __except(EXCEPTION_EXECUTE_HANDLER) {

 … /* Обработать FP-исключение. */

 _clearfp(); /* Сбросить исключение. */

 _controlfp(FPOld, 0xFFFFFFFF); /* Восстановить маску. */

}

В этом примере разрешены все возможные FP-исключения, кроме одного — EXCEPTION_FLT_STACK_CHECK, которое соответствует переполнению стека при выполнении операций над числами с плавающей точкой. Можно поступить и по-другому, разрешая отдельные исключения путем использования только выбранных масок исключений, например EM_OVERFLOW. Аналогичный код используется в программе 4.3 в контексте примера программного кода большего объема.