С учетом некоторого положительного значения y
, используемого в качестве приращения, наименьшее значение X
, для которого добавление y
не приводит к результату, большему X
, является наименьшей степенью 2, равной y
, деленной на половину «эпсилон» формата с плавающей точкой. Его можно рассчитать по:
Float Y = y*2/std::numeric_limits<Float>::epsilon();
int e;
std::frexp(Y, &e);
Float X = std::ldexp(.5, e);
if (X < Y) X *= 2;
Доказательство следует. Я предполагаю, что двоичная арифметика IEEE-754 с плавающей запятой использует округление до ближайшего числа, связанного с четностью.
Когда в арифметике IEEE-754 добавляются два числа, результатом является точный математический результат, округленный до ближайшего представимого значения в выбранном направлении.
Примечание к записи: текст в source code format
представляет значения и операции с плавающей точкой. Другой текст математический. Таким образом, x + y является точной математической суммой x и y , x
составляет x в Формат с плавающей точкой, и x+y
является результатом добавления x
и y
в операции с плавающей точкой. Также я буду использовать Float
для типа с плавающей точкой в C ++.
Учитывая число с плавающей точкой x , рассмотрите возможность добавления положительного значения y , используя арифметику с плавающей точкой, x+y
. При каких условиях результат будет превышать x ?
Пусть x 1 будет следующим значением, большим, чем x , представляемым в формате с плавающей запятой, и пусть x m будет средней точкой между x и x 1 . Если математическое значение x + y меньше, чем x m , тогда вычисление с плавающей запятой x+y
округляется в меньшую сторону, поэтому он производит x . Если x + y больше, чем x m , либо оно округляется и выдает x 1 , или выдает большее число, потому что y достаточно велико, чтобы переместить сумму за пределы x 1 . Если x + y равно x m , результат будет равен x или x 1 имеет четную младшую цифру. По причинам, которые мы увидим, это всегда x в ситуациях, имеющих отношение к этому вопросу, поэтому расчет округляется вниз.
Следовательно, x+y
дает результат, больший чем x тогда и только тогда, когда x + y превышает x m , что означает, что y превышает половину расстояния от x до x 1 . Обратите внимание, что расстояние от x до x 1 - это значение 1 в младшем разряде значенияи x
.
В двоичном формате с плавающей точкой с p цифрами в его значении, значение позиции младшей цифры в 2 1− p раз больше позиции значение старшей цифры. Например, если x равно 2 e , старший бит в его значении представляет 2 e , и младший бит представляет 2 e + 1− p .
Вопрос задает, учитывая y , что является наименьшим x , для которого x+y
не дает результат, больший чем x ? Это наименьшее значение x , для которого y не превышает половины значения младшего разряда значимого и x
.
Пусть 2 e будет значением позиции старшего бита значения и x . Тогда y ≤ ½ • 2 e + 1− p = 2 e - p , т. е. y • 2 p ≤ 2 e .
Поэтому дайn некоторое положительное значение y , наименьшее значение x , для которого x+y
не дает результат, больший чем x , имеет старший бит, 2 e , равный или превышающий y • 2 p . И на самом деле это должно быть ровно 2 e , потому что все остальные числа с плавающей запятой, чей старший бит имеет значение позиции 2 e , имеют другие биты установлены в их значениях, поэтому они больше. 2 e - это наименьшее число, для которого старший бит представляет 2 e .
Следовательно, x является наименьшей степенью двойки, равной или превышающей y • 2 p .
В C ++ std::numeric_limits<Float>::epsilon()
(из заголовка <limits>
) - это шаг от 1 до следующего представимого значения, то есть это 2 1− p . То есть y • 2 p равно y*2/std::numeric_limits<Float>::epsilon()
. (Эта операция является точной, если она не переполняется до ∞.)
Давайте присвоим это переменной:
Float Y = y*2/std::numeric_limits<Float>::epsilon();
Мы можем найти значение позиции, представленное старшим битом значения Y , используя frexp
(из заголовка <cmath>
), чтобы извлечь показатель степени из представления * с плавающей запятой Y
и ldexp
(также <cmath>
), чтобы применить этот показатель к новому значению и (.5
из-за шкалы, которую frexp
и ldexp
используют):
int e;
std::frexp(Y, &e);
Float X = std::ldexp(.5, e);
Тогда X - это степень двойки, и она меньше или равна Y . Это на самом деле наибольшая сила двух, не превышающая Y , поскольку следующая большая сила 2, 2 X , больше Y . Тем не менее, мы хотим наименьшую мощность двух не менее Y . Мы можем найти это с:
if (X < Y) X *= 2;
Результирующее X - это число, которое ищет вопрос.