Если я хочу проверить, что положительное плавание A действительно ли меньше, чем инверсия является квадратом другого положительного плавания B (в C99), что-то могло пойти не так, как надо, если B является очень маленьким?
Я мог предположить проверять его как
if(A<1/(B*B))
Но если бы B является достаточно маленьким, это возможно привело бы к бесконечности? Если бы это должно было произойти, то код все еще работал бы правильно во всех ситуациях?
В том же духе я мог бы сделать
if(1/A>B*B)
... который мог бы быть немного лучше, потому что B*B мог бы быть нулем, если B является маленьким (действительно ли это верно?)
Наконец, решение, что я не могу предположить быть неправым,
if(sqrt(1/A)>B)
то, которое я не думаю, когда-либо приводило бы к нулевому подразделению, но все еще могло бы быть проблематичным, если A близко к нулю.
Так в основном мои вопросы:
Править: для тех из Вас, кто задается вопросом, я закончил тем, что делал
if(B*A*B<1)
Я сделал это в том порядке, поскольку это визуально однозначно, какое умножение происходит сначала.
Если вы хотите обработать весь диапазон возможных значений 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 priori , что a
и ] b
являются положительными числами, для a * b * b
невозможно сгенерировать NaN, поэтому вам не нужно беспокоиться об этом условии. Единственное возможное неправильное поведение - это переполнение и недостаточное количество ресурсов, и мы уже учли их. Если вам нужно было поддержать случай, когда a
или b
может быть нулем или бесконечностью, тогда вам нужно быть несколько более осторожным.
Чтобы ответить на ваши прямые вопросы: (ответы предполагают арифметику IEEE-754)
Может ли 1 / X когда-либо быть бесконечностью, если X больше нуля (но мало)?
Да! Если x - небольшое положительное денормальное значение, тогда 1 / x
может переполниться и произвести бесконечность. Например, при двойной точности в режиме округления по умолчанию 1 / 0x1.0p-1024
будет переполняться.
Может ли X * X когда-либо быть нулевым, если X больше нуля?
Да! При двойной точности в режиме округления по умолчанию все значения x меньше 0x1.0p-538
(то есть 2 ** - 578
в шестнадцатеричном формате C99) или около того имеют это свойство .
Будут ли сравнения с бесконечностью работать так, как я ожидал?
Да! Это одна из лучших особенностей IEEE-754.
Хорошо, репост в качестве ответа.
Попробуйте использовать арифметически эквивалентное сравнение, например if (A * B * B <1.)
. Однако у вас могут возникнуть проблемы с действительно большими числами.
Внимательно изучите IEEE 754 для своих угловых случаев.
Вы хотите избежать деления, поэтому хитрость заключается в изменении уравнения. Вы можете умножить обе стороны первого уравнения на (b*b), чтобы получить:
b*b*a < 1.0
Это не будет иметь никаких делений, так что все будет в порядке.
Деление как таковое не так уж плохо. Однако стандартные типы IEEE 754 FP допускают больший диапазон отрицательных экспонент, чем положительных, из-за денормализации чисел. Например, float
варьируется от 1.4×10-45 до 3.4×10-38, поэтому вы не можете взять обратную величину 2×10-44.
Поэтому, как предлагает Джереми, начните с умножения A на B, где одно из чисел имеет положительную экспоненту, а другое - отрицательную, чтобы избежать переполнения.
Вот почему A*B*B<1
является правильным ответом.