Проверить наличие нуля или денормализованного числа в С++

В настоящее время у меня есть код, в котором я должен нормализовать вектор двойников (разделить каждый элемент на сумму). При отладке я иногда вижу, что все элементы в векторе равны 0.0. Если я возьму сумму элементов, то получу либо 0,0, либо 4,322644347104e-314#DEN (которое, как я недавно узнал, было денормализованным числом). Я хотел бы предотвратить нормализацию вектора для случаев, когда сумма равна 0,0 или денормализованному числу. Единственный способ, которым я мог бы обработать эти два случая, - это проверить, меньше ли сумма, чем «эпсилон», где эпсилон — это небольшое число (но я не уверен, насколько маленьким сделать эпсилон).

У меня есть 2 вопроса:

  1. Как лучше учесть эти случаи?
  2. Зависит ли значение денормализованной числовой машины?

person A-A    schedule 18.07.2011    source источник
comment
Я думал нормализация означает ==› "divide by average of sum"   -  person iammilind    schedule 18.07.2011
comment
Множество вещей, которые вы могли бы назвать нормализацией. Например, вы можете захотеть разделить на p-й корень суммы p-х степеней элементов...   -  person Kerrek SB    schedule 18.07.2011
comment
@iammilind: Нормализация обычно означает деление на квадратный корень из суммы квадратов. Или по какой-то другой норме L‹sub›p‹/sub›.   -  person David Hammen    schedule 18.07.2011
comment
@Kerrek: Lp-норма на самом деле будет p-м корнем суммы p-х степеней абсолютных значений элементов. То же самое только для четных p. В частности, если вам нужен L₁, вам нужна сумма абсолютных значений. Но более распространенная и необычайно более полезная норма (поскольку она неизменна при ортогональных преобразованиях) — это действительно L₂, где вам нужен квадратный корень из суммы квадратов.   -  person leftaroundabout    schedule 19.07.2011
comment
@leftaroundabout: Да, вы, конечно, правы - я думаю, мне просто лень печатать :-) Думаю, вам всегда нужно абсолютное значение, если вы хотите разрешить комплексные числа...   -  person Kerrek SB    schedule 19.07.2011


Ответы (3)


#include <limits>
#include <cmath>
double epsilon = std::numeric_limits<double>::min();
if (std::abs(sum) < epsilon) {
  // Don't divide by sum.
}
else {
  // Scale vector components by sum.
}

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

double sum = 0;
for (unsigned int ii = 0; ii < vector_size; ++ii) {
    sum += vector[ii]*vector[ii];
}
sum = std::sqrt(sum);

Есть три проблемы с вышеизложенным.

  1. Если какой-либо из этих векторных компонентов больше по величине, чем sqrt(max_double), вы получите бесконечность.
  2. Если какой-либо из этих векторных компонентов меньше по величине, чем sqrt(min_double), вы получите потерю значимости.
  3. Даже если числа хорошо себя ведут (от 2*10-154 до 10154), описанное выше проблематично, если величины сильно различаются (коэффициент 10 6 подойдет). Вам нужна более сложная функция гипотенузы, если это так.
person David Hammen    schedule 18.07.2011
comment
Судя по формулировке ОП, я думаю, вам может понадобиться min(), а не denorm_min(). - person Oliver Charlesworth; 18.07.2011

C99 предоставляет fpclassify для обнаружения денормализованного числа. Он также поставляется с C++0x и Boost.Math.

// C++0x
#include <cmath>
using std::fpclassify;

// Boost
//#include <boost/math/special_functions/fpclassify.hpp>
//using boost::math::fpclassify;

if(fpclassify(sum) == FP_SUBNORMAL) {
    // ...
}
person Luc Danton    schedule 18.07.2011

Вы можете использовать флаг при подсчете суммы, чтобы убедиться, что не каждый элемент равен 0.

person ascanio    schedule 18.07.2011