Вот две реализации функций интерполяции. Аргумент u1 всегда находится между 0. и 1..
#include <stdio.h>
double interpol_64(double u1, double u2, double u3)
{
return u2 * (1.0 - u1) + u1 * u3;
}
double interpol_80(double u1, double u2, double u3)
{
return u2 * (1.0 - (long double)u1) + u1 * (long double)u3;
}
int main()
{
double y64,y80,u1,u2,u3;
u1 = 0.025;
u2 = 0.195;
u3 = 0.195;
y64 = interpol_64(u1, u2, u3);
y80 = interpol_80(u1, u2, u3);
printf("u2: %a\ny64:%a\ny80:%a\n", u2, y64, y80);
}
На строгой платформе IEEE 754 с 80-битным long double s все вычисления в interpol_64() выполняются в соответствии с двойной точностью IEEE 754 и в interpol_80() в 80-битной расширенной точности.
Программа печатает:
u2: 0x1.8f5c28f5c28f6p-3
y64:0x1.8f5c28f5c28f5p-3
y80:0x1.8f5c28f5c28f6p-3
Меня интересует свойство "результат, возвращаемый функцией, всегда находится между u2 и u3". Это свойство ложно interpol_64(), как показано значениями в main() выше.
Имеет ли свойство возможность быть верным для interpol_80()? Если это не так, что такое встречный пример? Помогает ли это, если мы знаем, что u2 != u3 или что между ними существует минимальное расстояние? Есть ли способ определить значительную ширину для промежуточных вычислений, в которых гарантируется, что свойство будет истинным?
РЕДАКТИРОВАТЬ: во всех случайных значениях, которые я пробовал, свойство хранилось, когда промежуточные вычисления выполнялись с расширенной точностью внутри. Если interpol_80() принял аргументы long double, было бы относительно легко построить встречный пример, но здесь речь идет конкретно о функции, которая принимает аргументы double. Это значительно усложняет построение встречного примера, если он есть.
Примечание: компилятор, генерирующий инструкции x87, может генерировать один и тот же код для interpol_64() и interpol_80(), но это касается моего вопроса.