Я читаю Вот и пришел к выводу, что минимальное (абсолютное …) значение для двойного 1.7e-308
но мой код зацикливается навсегда:
for (double d(-1.0); d <= 1.0; d+=1.7e-308)
{
}
Редактировать: Я хочу зациклить от -1,0 до 1,0 с наименьшим возможным шагом.
TL; DR: разрыв между числами, которые компьютер может хранить в числах с плавающей запятой, увеличивается с увеличением числа. Ваш минимальный номер действителен только тогда, когда номер равен нулю. В какой-то момент попытка добавить такое маленькое число ничего не дает, так как не может представить такое небольшое изменение числа.
Это связано с тем, что разрыв между каждым представимым числом с плавающей запятой увеличивается с увеличением величины.
Разница между 1 и ближайшим представимым меньше 1 составляет около 1,11 × 10.-16, намного больше, чем значение, которое вы пытаетесь добавить.
То, что происходит, — то, что двойник сохраняется с одним набором битов для знака числа, другой, чтобы представить само число (как целое число без знака) и множитель как показатель степени 2.
Когда вы близки к нулю, показатель степени очень мал, что делает одно изменение фактического числа, хранящегося меньше, чем если ваш показатель гораздо больше.
Например, (4 — 3) × 299 очевидно будет больше (4 — 3) × 2-99, что то, что в основном происходит.
Когда вы начнете добавлять, вы обнаружите, что в конечном итоге, добавив 1,7 × 10-308 ничего не делает, поскольку ваше текущее значение является ближайшим представимым значением для сложения, которое продолжается и продолжается в бесконечном цикле.
Обратите внимание, что это только когда режим округления установлен на roundTiesToEven или roundTiesToAway, где они округляются до ближайшего представимого значения, за исключением случаев, когда они одинаково близки.
Если вы вручную установили режим округления на roundTowardPositive, вызвав std::fesetround
с FE_UPWARD
заранее (как отметил Марк Глисс), если вы добавите любое положительное число (кроме нуля), оно будет увеличиваться. Я думаю, что roundTowardNegative и roundTowardZero говорят сами за себя.
Кстати, даже если вы замените это на 1.11 × 10-16, вам все равно придется пройти 18 014 398 509 481 984 (18 квадриллионов) итераций, прежде чем закончить. Если вы на самом деле просматриваете каждое из значений от -1 до 1, это 9 223 372 036 854 775 809 (9 квинтиллионов) итераций. Удачи.
В C ++ 11 / C99 вы можете использовать nextafter
чтобы получить следующий представительный номер.
#include <cmath>
for (double d(-1.0); d <= 1.0; d = std::nextafter(d, 1.0) )
{
}
Кроме того, если я сделал математику правильно, есть приблизительно 10 ^ 18 представимых значений между -1 и 1.
При разумном допущении 10 ^ 6 циклов в секунду для выполнения этого вычисления потребуется 32000 лет.
Я не думаю, что минимальное значение для double равно 1.7e-308, это, вероятно, больше похоже на -1.7 e308, 1.7e-308 — это действительно небольшое число, но не близко к отрицательной бесконечности. Это очень маленькое число, близкое к 0.