Ответ 1
Underflow - это не только вопрос диапазона, но и точность/округление.
7.12.1 Обработка условий ошибок
Результат заканчивается, если величина математического результата настолько мала, что математический результат не может быть представлен без особой ошибки округления в объекте указанного типа. C11 §7.12.1 6
1.777e-308, преобразованный в ближайший binary64 0x1.98e566222bcfcp-1023, имеет значение (0x198E566222BCFC, 7193376082541820), которое кратна 10. Таким образом, деление на 10 точнее. Ошибка округления.
Мне кажется, что это проще для демонстрации с шестнадцатеричной нотацией. Обратите внимание, что деление на 2 всегда точное, за исключением наименьшего значения.
#include <float.h>
#include <stdio.h>
#include <fenv.h>
#include <math.h>
int uf_test(double x, double denominator){
printf("%.17e %24a ", x, x);
feclearexcept(FE_ALL_EXCEPT);
double y=x/denominator;
int uf = !!fetestexcept(FE_UNDERFLOW);
printf("%-24a %s\n", y, uf ? "Underflow" : "");
return uf;
}
int main(void) {
uf_test(DBL_MIN, 2.0);
uf_test(1.777e-308, 2.0);
uf_test(1.77e-308, 2.0);
uf_test(DBL_TRUE_MIN, 2.0);
uf_test(pow(2.0, -1000), 10.0);
uf_test(DBL_MIN, 10.0);
uf_test(1.777e-308, 10.0);
uf_test(1.77e-308, 10.0);
uf_test(DBL_TRUE_MIN, 10.0);
return 0;
}
Выход
2.22507385850720138e-308 0x1p-1022 0x1p-1023
1.77700000000000015e-308 0x1.98e566222bcfcp-1023 0x1.98e566222bcfcp-1024
1.77000000000000003e-308 0x1.97490d21e478cp-1023 0x1.97490d21e478cp-1024
4.94065645841246544e-324 0x1p-1074 0x0p+0 Underflow
// No underflow as inexact result is not too small
9.33263618503218879e-302 0x1p-1000 0x1.999999999999ap-1004
// Underflow as result is too small and inexact
2.22507385850720138e-308 0x1p-1022 0x1.99999999999ap-1026 Underflow
// No underflow as result is exact
1.77700000000000015e-308 0x1.98e566222bcfcp-1023 0x1.471deb4e8973p-1026
1.77000000000000003e-308 0x1.97490d21e478cp-1023 0x1.45d40a818394p-1026 Underflow
4.94065645841246544e-324 0x1p-1074 0x0p+0 Underflow