Назначение функции неизвестно, что она делает? - PullRequest
0 голосов
/ 11 января 2020

Может ли кто-нибудь помочь мне понять, что делает эта функция, если ввод представляет собой комплексное число a+bi и real = a, imag = b

Я понятия не имею, что это могут быть вычисления, но, возможно, я упускаю что-то очевидное ?

double function(double real, double imag)
{
  double y;
  double a;
  double b;
  a = fabs(real);
  b = fabs(imag);
  if (a < b) 
  {
    a /= b;
    y = b * sqrt(a * a + 1.0);
  } 
  else if (a > b) 
  {
    b /= a;
    y = a * sqrt(b * b + 1.0);
  } 
  else if (b == NAN) 
  {
    y = b;
  } 
  else 
  {
    y = a * sqrt(2);
  }

  return y;
}

1 Ответ

3 голосов
/ 11 января 2020

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

Рассмотрим комплексное число a + b i, где a и b - это значения, присвоенные a и b в первых нескольких строках функции. Его величина √ ( a 2 + b 2 ). Однако, если a или b велико, вычисление с плавающей запятой a*a может переполнить конечный диапазон формата с плавающей запятой и привести к бесконечности (∞), даже если величина находится в пределах досягаемости. В качестве простого примера, пусть a будет 2 1000 и b будет 0. Тогда величина √ (2 2000 + 0) = 2 1000 , но вычисление sqrt(a*a + b*b) дает бесконечность. (Поскольку a*a переполнено и выдает ∞, а остальные вычисления также производят ∞.)

Код пытается решить эту проблему путем деления меньшего из a и b по большому счету с использованием математически эквивалентного вычисления, но не переполнения. Например, если a < b имеет значение true, выполняется:

a /= b;
y = b * sqrt(a * a + 1.0);

Тогда a /= b дает значение меньше 1, поэтому все вычисления до последнего находятся в безопасном пределах в пределах конечного диапазона с плавающей запятой: a * a меньше 1, a * a + 1.0 меньше 2 и sqrt(a * a + 1.0) меньше 1,42. Когда мы умножим на b, конечный результат может переполниться до ∞. Это может произойти по двум причинам: величина a + b i может превысить конечный диапазон с плавающей точкой, поэтому переполнение является правильным. Или же округление в предыдущих расчетах могло привести к тому, что sqrt(a * a + 1.0) будет немного больше математического результата и достаточным для переполнения b * sqrt(a * a + 1.0), когда фактическое значение величины находится в пределах диапазона. Поскольку это не является нашей целью, я не буду более подробно анализировать этот случай в этом ответе.

Помимо этой проблемы округления, первые два случая хороши. Однако этот код неверен:

else if (b == NAN)

Согласно IEEE-754 2008 5.11 и IEEE-754 1985 5.7, NaN не меньше, равен или больше любого операнда, включая самого себя. неупорядочено . Это означает, что b == NAN должно возвращать false, если используется IEEE-754. C 2018 не требует использования IEEE-754, но в сноске 22 (в п. 5.2.4.2.2 4) говорится, что, если IEC 60559: 1989 (фактически IEEE-754) не поддерживается, термины «Тихий NaN» и «сигнальный NaN» в стандарте C предназначены для применения к кодировкам с аналогичным поведением. И 7.12 5 говорит нам, что NAN расширяется до float, представляющего тихий NaN. Таким образом, в b == NAN, NAN должен вести себя как NaN IEEE-754, и поэтому b == NAN должен давать 0 для false.

Следовательно, этот код, определяемый else if (b == NAN), никогда не выполняется:

y = b;

Вместо этого выполнение переходит к else, который выполняет:

y = a * sqrt(2);

Если a - это NaN, результатом является NaN, по желанию. Однако, если a - это число, а b - это NaN, в результате получается число, когда желаемым результатом будет NaN. Таким образом, код не работает.

...