2016-07-25 7 views
9

У меня есть число, которое может быть нулями. Я делю на это число, поэтому я хочу проверить, является ли оно нулевым, чтобы предотвратить NaN и бесконечность. Возможно ли, что я все еще создаю NaNs/бесконечность из-за ошибок округления в разделе?Может деление ненулевым по-прежнему создавать нан/бесконечность

double x; // might be zero 
double y; 

if(x != 0) return y/x; 

EDIT

Спасибо за ответы. Тогда я добавлю некоторые подзапросы.

1) если ни x, ни y не являются NaN/+ inf или -inf, будет ли разделение, результатом которого является -inf/+ inf, больше циклов процессора или любого другого нежелательного поведения? (Может ли это сбой?)

2) есть ли способ предотвратить деление от бесконечности? Использование смещений и т. Д.

+0

они могут быть бесконечности или даже нан – EFenix

+2

Если 'y' уже NaN, то результат также будет NaN. –

+2

Добавляем больше вопросов после того, как кто-то уже опубликовал ответ (3, в данном случае) невелик - он делает эти ответы незавершенными, хотя они не были, когда они были написаны. –

ответ

6

Разделение очень небольшого числа на очень большое число или умножение двух очень больших чисел может привести к «бесконечности». Разделение бесконечности на другую бесконечность даст NaN. Например, (1E300/1E-300)/(1E300/1E-300) или (1E300*1E300)/(1E300*1E300) будут давать NaN.

9

Может деление ненулевого еще создать нан/бесконечность

Да.

Если IEEE-754 следует затем:

  • Если один из операндов равен NaN, то результат будет NaN.
  • Если и числитель, и денумератор бесконечны, результатом является NaN.
  • Если только числитель бесконечен, результат бесконечности.
  • Если деление на малый денумератор (или большой числитель) переполняется, результат может быть бесконечным, в зависимости от текущего режима округления.

Правила других изображений могут отличаться.


2) есть способ предотвратить DeVision от приводя к бесконечности

Это должно пройти долгий путь в предотвращении, что:

#include <cfenv> 
#include <cassert> 
#include <cmath> 
#include <limits> 

// ... 

static_assert(std::numeric_limits<decltype(x)>::is_iec559, "Unknown floating point standard."); 
#pragma STDC FENV_ACCESS ON 
int failed = std::fesetround(FE_TOWARDZERO); 
assert(!failed); 
if(x != 0 && std::isfinite(x) && std::isfinite(y)) 
    return y/x; 
else 
    throw std::invalid_argument("informative message"); 

Некоторые компиляторы могут потребоваться нестандартные параметры для обеспечения полного соответствия IEEE 754 (-frounding-math на GCC).

+0

«в зависимости от текущего режима округления» Это очень важно, см. 'Fegetround' /' fesetround'. Если вы округлите до нуля, вы никогда не сможете получить inf, если вы разделите его на конечные отличные от нуля. – sbabbi

+0

Я чувствую, что fesetround - это ничего, что нужно было бы назвать в функции, чувствительной к производительности, верно? –

+0

@ruhigbrauner, возможно, нет. Возможно, вы захотите переместить его за пределы любых горячих контуров. – user2079303

5

Да, просто посмотрите на код ниже

#include <iostream> 
int main() 
{ 
    double x = 1, y = 2; 
    while (y != 0) { 
     std::cout << y << " " << x/y << std::endl; 
     y /= 2; 
    } 
} 

в какой-то момент вы получите:

8.9003e-308 1.12356e+307 
4.45015e-308 2.24712e+307 
2.22507e-308 4.49423e+307 
1.11254e-308 8.98847e+307 
5.56268e-309 inf 
2.78134e-309 inf 
1.39067e-309 inf 
6.95336e-310 inf