Я пытаюсь определить диапазон различных типов с плавающей запятой. Когда я прочитаю этот код:
Ответ 2
OP:... почему автор добавил 1111e28
в переменную fltest
?
A: [Изменить] Для работы кода с использованием float
, 1111e28
или 1.111e31
это значение дельта требует тщательного выбора. Он должен быть достаточно большим, чтобы, если fltest
было FLT_MAX
, сумма fltest + delta
переполнилась бы и стала бы float.infinity
. С круглым до ближайшего режима это FLT_MAX*FLT_EPSILON/4
. На моей машине:
min_delta 1.014120601e+31 1/2 step between 2nd largest and FLT_MAX
FLT_MAX 3.402823466e+38
FLT_EPSILON 8.388608000e+06
FLT_MAX*FLT_EPSILON 4.056481679e+31
delta
должен быть достаточно малым, поэтому, если f1test
является вторым по величине числом, добавив delta, не будет суммироваться вплоть до float.infinity
и пропустить FLT_MAX
. Это 3x min_deltap >
max_delta 3.042361441e+31
Итак 1.014120601e+31 <= 1111e28 < 3.042361441e+31
.
@david.pfx Да. 1111e28 - симпатичное число, и оно находится в диапазоне.
Примечание. Осложнения возникают, когда математика и ее промежуточные значения, даже если переменные float
могут вычисляться при более высокой точности, например double
. Это разрешено в C и управлении с помощью FLT_EVAL_METHOD
или очень тщательного кодирования.
1111e28
- это любопытное значение, которое имеет смысл, если автор все готов знал общий диапазон FLT_MAX
.
Ожидается, что приведенный ниже код будет циклически повторяться (24946069 на одной тестовой платформе). Надеемся, что значение fltest
в конечном итоге станет "бесконечным". Тогда f1
станет NaN как разность Бесконечности - Бесконечности. Цикл while заканчивается как Nan!= 0.0. @ecatmur
while (fl == 0.0) {
last = fltest;
fltest = fltest + 1111e28;
fl = (fl + fltest) - fltest;
}
Цикл, если он выполняется с небольшим шагом, достигнет точного ответа. Для обеспечения этого необходимы предварительные знания FLT_MAX
и FLT_EPSILON
.
Проблема заключается в том, что C не определяет диапазон FLT_MAX
и DBL_MAX
, кроме как они должны быть не менее 1E+37
. Поэтому, если максимальное значение было довольно большим, значение приращения 1111e28 или 1111e297 не повлияло бы. Пример: dbltest = dbltest + 1111e297;
, для dbltest = 1e400
, конечно, не будет увеличиваться на 1e400, если dbltest
сто десятичных цифр точности.
Если DBL_MAX
было меньше 1111e297, метод также терпит неудачу. Примечание. На простых платформах в 2014 году нет ничего удивительного в том, чтобы найти double
и float
как один 4-байтовый IEEE binary32) В первый раз, хотя цикл, dbltest
становится бесконечным, и цикл останавливается, сообщая "Максимальный диапазон двойной переменной: 0.000000e + 00".
Существует множество способов эффективного получения максимального значения точки плавания. Далее следует образец, который использует случайное начальное значение, чтобы показать его устойчивость к потенциальному варианту FLT_MAX
.
float float_max(void) {
float nextx = 1.0 + rand()/RAND_MAX;
float x;
do {
x = nextx;
nextx *= 2;
} while (!isinf(nextx));
float delta = x;
do {
nextx = x + delta/2;
if (!isinf(nextx)) {
x = nextx;
}
delta /= 2;
} while (delta >= 1.0);
return x;
}
isinf()
является новой функцией C. Достаточно просто, чтобы свернуть свой собственный, если нужно.
В re: комментарий @didierc
[Изменить]
Точность a float
и double
подразумевается с помощью "epsilon": "разница между 1 и наименьшим значением больше 1, которое представимо в
заданный тип с плавающей запятой... ". Максимальные значения следуют за
FLT_EPSILON 1E-5
DBL_EPSILON 1E-9
За комментарий @Pascal Cuoq. "... 1111e28 выбирается больше FLT_MAX * FLT_EPSILON.", 1111e28 должен быть как минимум FLT_MAX*FLT_EPSILON
, чтобы воздействовать на добавление цикла, но достаточно мал, чтобы точно достичь числа до бесконечности. Опять же, для этого определения необходимы предварительные знания FLT_MAX
и FLT_EPSILON
. Если эти значения известны заранее, то простой код мог бы быть:
printf("Maximum range of float variable: %e\n", FLT_MAX);