Могут ли плохие вещи случиться при делении 1 / очень маленького числа? - PullRequest
7 голосов
/ 02 июня 2010

Если я хочу проверить, что положительное число с плавающей точкой A меньше, чем обратный квадрат другого положительного числа с плавающей точкой B (в C99), может ли что-то пойти не так, если B очень мало?

Я мог бы представить, проверяя это как

if(A<1/(B*B))

Но если B достаточно мало, может ли это привести к бесконечности? Если это произойдет, будет ли код работать правильно во всех ситуациях?

В том же духе, я мог бы сделать

if(1/A>B*B)

... что может быть немного лучше, потому что B * B может быть нулем, если B мало (это правда?)

Наконец, решение, которое я не могу себе представить, является неправильным:

if(sqrt(1/A)>B)

, который, я думаю, никогда не приведет к нулевому делению, но все еще может быть проблематичным, если A близко к нулю.

Так что в основном мои вопросы:

  • Может ли 1 / X быть бесконечностью, если X больше нуля (но мало)?
  • Может ли X * X быть нулем, если X больше нуля?
  • Будет ли сравнение с бесконечностью работать так, как я ожидал?

РЕДАКТИРОВАТЬ: для тех из вас, кто задается вопросом, я закончил делать

if(B*A*B<1) 

Я сделал это в том порядке, поскольку визуально однозначно, какое умножение происходит первым.

Ответы [ 4 ]

7 голосов
/ 02 июня 2010

Если вы хотите обрабатывать весь диапазон возможных значений A и B, то вам нужно быть немного осторожнее, но на самом деле это не так уж сложно.

Предложение использования a*b*b < 1. является хорошим; если b настолько мал, что a*b*b падает до нуля, то a обязательно меньше, чем 1./(b*b). И наоборот, если b настолько велико, что a*b*b переполняется до бесконечности, то условие (правильно) не будет выполнено. ( Potatoswatter правильно указывает в комментарии к другому сообщению, что это не работает правильно, если вы напишите b*b*a, потому что b*b может переполниться до бесконечности, даже если условие должно будет верно, если a окажется ненормальным. Однако в C умножение ассоциируется слева направо, так что это не проблема, если вы напишите его a*b*b и ваша платформа придерживается разумной числовой модели.)

Поскольку вы знаете априори , что a и b являются положительными числами, a*b*b не может сгенерировать NaN, поэтому вам не нужно беспокоиться об этом условии. Переполнение и переполнение являются единственно возможными нарушениями, и мы уже учли их. Если вам нужно поддержать случай, когда a или b могут быть равны нулю или бесконечности, вам нужно быть более осторожным.

Чтобы ответить на ваши прямые вопросы: (ответы предполагают арифметику IEEE-754)

Может ли 1 / X быть бесконечностью, если X больше нуля (но мало)?

Да! Если x - это небольшое положительное ненормальное значение, то 1/x может переполниться и привести к бесконечности. Например, при двойной точности в режиме округления по умолчанию 1 / 0x1.0p-1024 будет переполнен.

Может ли X * X быть нулем, если Х больше нуля?

Да! С двойной точностью в режиме округления по умолчанию все значения x меньше 0x1.0p-538 (то есть 2**-578 в шестнадцатеричном формате C99) или около того имеют это свойство.

Будет ли сравнение с бесконечностью работать так, как я ожидал?

Да! Это одна из лучших функций IEEE-754.

5 голосов
/ 02 июня 2010

ОК, перепост как ответ.

Попробуйте использовать арифметически эквивалентное сравнение, например if ( A*B*B < 1. ). Однако у вас могут возникнуть проблемы с действительно большими числами.

Внимательно посмотрите на IEEE 754 для ваших угловых чехлов.

3 голосов
/ 02 июня 2010

Вы хотите избежать делений, поэтому задача состоит в том, чтобы изменить уравнение. Вы можете умножить обе части вашего первого уравнения на (b * b), чтобы получить:

b*b*a < 1.0

У этого не будет никаких делений, поэтому должно быть в порядке.

1 голос
/ 02 июня 2010

Деление само по себе не так уж плохо. Однако стандартные типы IEEE 754 FP допускают больший отрицательный отрицательный диапазон показателей, чем положительный, из-за денормализованных чисел. Например, float колеблется от 1,4 & times; 10 -45 до 3,4 & times; 10 -38 , поэтому вы не можете принять обратное значение 2 & times; 10 -44 .

Поэтому, как предлагает Джереми, начните с умножения A на B, где у одного положительный показатель степени, а у другого отрицательный показатель, чтобы избежать переполнения.

Вот почему A*B*B<1 является правильным ответом.

...