Обратите внимание, что в соответствии со стандартом C два значения NaN никогда не сравниваются одинаково, даже если они имеют одинаковую битовую комбинацию.Если вы собираетесь сортировать данные с помощью NaN, вам необходимо:
- Решить, где следует сортировать значения NaN по правильным значениям (обычно это варианты «до отрицательной бесконечности» или «после положительной бесконечности»).
- Используйте гораздо более деликатные тесты, чем простое сравнение
a > b
.
Соответствующая информация разбросана по стандарту C11.Например:
Вы, вероятно, организуете создание функции (возможно, функции inline
, если только вы не собираетесь передавать ее функции, подобной qsort()
), которая сравнивает два значения с плавающей запятой соответствующего типа (выглядиткак вы используете float
), который использует макрос классификации isnan()
или isnanf()
, чтобы определить, является ли одно или оба значения NaN.Функция, вероятно, вернет значение, указывающее равенство, если оба значения являются NaN, но если одно является NaN, возвращаемое значение поместит его раньше или позже другого, в зависимости от порядка, в котором вы хотите, чтобы NaN появлялись, и вернет значение.соответствующее значение для сравнения других значений (нормальные значения, нули, бесконечности, субнормальные числа) - для регулярных значений и бесконечностей требуются только операторы регулярного сравнения, если только вам не нужно правильно сортировать отрицательные нули по положительным нулям.
ДляНапример, написание функции, которая работает с qsort()
(и с использованием типа double
вместо float
), приводит к чему-то вроде этого, предполагая, что числа должны быть отсортированы в порядке возрастания и что NaN должны сравниваться меньше, чем любое другое значение.Код включает тестовый код для считывания данных со стандартного ввода, распечатки, сортировки и повторной печати.
#include <math.h>
/* Belongs in a header! */
extern int cmp_double(const void *v1, const void *v2);
/* Sort doubles, with NaNs coming first */
/* Switch return values -1 and +1 after testing n1, n2 to sort NaNs last */
int cmp_double(const void *v1, const void *v2)
{
double d1 = *(const double *)v1;
double d2 = *(const double *)v2;
int n1 = isnan(d1);
int n2 = isnan(d2);
if (n1 && n2)
return 0;
if (n1)
return -1;
if (n2)
return +1;
if (d1 < d2)
return -1;
if (d1 > d2)
return +1;
// The values are 'equal', but …
if (d1 != 0.0)
return 0;
// They're both zero, but they could have different signs
int s1 = signbit(d1);
int s2 = signbit(d2);
if (s1 != s2)
return (s1) ? -1 : +1;
return 0;
}
#include <stdio.h>
#include <stdlib.h>
static void dump_doubles(const char *tag, int num, double values[num])
{
printf("%s (%d):\n", tag, num);
int line_len = 0;
for (int i = 0; i < num; i++)
{
int n = printf(" %+12.4f", values[i]);
if (n <= 0)
break;
line_len += n;
if (line_len >= 60)
{
line_len = 0;
putchar('\n');
}
}
if (line_len > 0)
putchar('\n');
}
int main(void)
{
enum { NUM_VALUES = 50 };
double values[NUM_VALUES];
int i = 0;
for (i = 0; i < NUM_VALUES; i++)
{
if (scanf("%lf", &values[i]) != 1)
break;
}
dump_doubles("Before sort", i, values);
qsort(values, i, sizeof(values[0]), cmp_double);
dump_doubles("After sort", i, values);
return 0;
}
Обратите внимание на тестирование, необходимое для сортировки -0.0 до +0.0!
Рассмотрим входные данные:
3023.421800 9033.902200 nan -9370.952500 3088.884900 6829.135400 0
-0.000000 -inf -5267.546800 -8784.373300 5663.944600 -9728.231300 inf
-inf -5373.038600 4282.941600 6245.734200 -5533.975400 nan 8445.713600
+inf -9108.960400 -3796.671200 nan -2363.851300 877.460400 9936.416900
-3480.867400
Выходные данные:
Before sort (29):
+3023.4218 +9033.9022 nan -9370.9525 +3088.8849
+6829.1354 +0.0000 -0.0000 -inf -5267.5468
-8784.3733 +5663.9446 -9728.2313 +inf -inf
-5373.0386 +4282.9416 +6245.7342 -5533.9754 nan
+8445.7136 +inf -9108.9604 -3796.6712 nan
-2363.8513 +877.4604 +9936.4169 -3480.8674
After sort (29):
nan nan nan -inf -inf
-9728.2313 -9370.9525 -9108.9604 -8784.3733 -5533.9754
-5373.0386 -5267.5468 -3796.6712 -3480.8674 -2363.8513
-0.0000 +0.0000 +877.4604 +3023.4218 +3088.8849
+4282.9416 +5663.9446 +6245.7342 +6829.1354 +8445.7136
+9033.9022 +9936.4169 +inf +inf