#floating-point #numerical #x87
#с плавающей запятой #числовой #x87
Вопрос:
Предположим, у меня есть два числа с плавающей запятой, x
и y
, причем их значения очень близки.
Существует дискретное количество чисел с плавающей запятой, которые можно представить на компьютере, поэтому мы можем перечислять их в порядке возрастания: f_1, f_2, f_3, ...
. Я хочу найти расстояние x
и y
в этом списке (т. Е. Находятся ли они на расстоянии 1, 2, 3, … или n
дискретных шагов друг от друга?)
Возможно ли это сделать, используя только арифметические операции ( -*/
) и не рассматривая двоичное представление? Меня в первую очередь интересует, как это работает на x86.
Является ли следующее приближение правильным, предполагая, что y > x
и что x
и y
находятся всего в нескольких шагах (скажем, < 100) друг от друга? (Вероятно, нет …)
(y-x) / x / eps
Здесь eps
обозначает машинный эпсилон. (Машинный эпсилон — это разница между 1.0 и следующим наименьшим числом с плавающей запятой.)
Комментарии:
1. @ShreevatsaR, вот почему я
x
тоже разделил на (не толькоeps
).(y-x) / x / eps = (y-x) / (x*eps)
. Хотя я все еще не уверен, что это правильно.2. Ах, ладно, извините. Ваша формула может быть правильной; давайте подумаем.
Тем временем вы можете прочитать, что каждый специалист по информатике должен знать об арифметике с плавающей запятой .
Ответ №1:
Числа с плавающей запятой лексикографически упорядочены, поэтому:
int steps(float a, float b){
int ai = *(int*)amp;a; // reinterpret as integer
int bi = *(int*)amp;b; // reinterpret as integer
return bi - ai;
}
steps(5.0e-1, 5.0000054e-1); // returns 9
Такой метод используется при сравнении чисел с плавающей запятой.
Комментарии:
1. пока числа > 0. Некоторая логика все еще необходима для покрытия отрицательных значений и 0 (включая «-0»).
2. 1, Интересное наблюдение о лексикографическом упорядочении. Что я действительно хотел бы знать, хотя все еще, возможно ли это, используя только арифметические операции с плавающей запятой. Язык, который я использовал, когда задавал этот вопрос, не поддерживает переосмысление битовых шаблонов как другого типа.
3. @Szabolcs какой язык вы использовали? Я написал код на C , но он должен работать на C и D.
4. На самом деле мой вопрос был не о том, как это сделать на этом языке, мне просто было любопытно, можно ли это сделать, используя только операции с плавающей запятой, не прибегая каким-либо образом к изучению битового представления числа с плавающей запятой. Я использовал Mathematica. Даже если это не прямой ответ на мой вопрос, ваш ответ был интересным, и я ценю это (особенно учитывая, сколько лет вопросу).
Ответ №2:
Я думаю, вам не обязательно проверять двоичное представление напрямую, но вы должны полагаться на него, чтобы получить точный ответ.
Начните с использования frexp() для разбиения x на экспоненту exp
и мантиссу. Я считаю, что следующее значение с плавающей запятой больше x x eps * 2^(exp-1)
. («-1» означает, что frexp возвращает мантиссу в диапазоне [1/2, 1), а не [1, 2).)
Если x и y имеют одинаковый показатель, вы в основном закончили. В противном случае вам нужно подсчитать, сколько шагов приходится на степень 2, что просто 1.0/eps
. Другими словами, количество шагов между 2 ^ n и 2 ^(n 1) равно 1.0/eps
.
Итак, для y > x подсчитайте, сколько шагов от x до следующей степени двойки; затем подсчитайте, сколько еще шагов потребуется, чтобы получить наибольшую степень, равную 2, меньшую, чем y; затем подсчитайте, сколько еще шагов потребуется, чтобы перейти оттуда к y . Я думаю, все это довольно легко выразить в терминах eps
.