Я пытаюсь рассчитать разницу между данным двойным и следующим представимым двойным числом (или минимальным двойным числом, добавление которого к заданному числу изменяет значение)
#include <stdio.h>
#include <math.h>
double f1(double a)
{
double diff=1.0,num=fabs(a);
while(num+(diff/2.0)>num )
{
diff/=2.0 ;
}
while(num+(diff*2.0)==num)
{
diff*=2.0 ;
}
return diff ;
}
double f2(double a)
{
int e=log2(fabs(a)) ;
double diff=pow(2,-52+e) ;
return diff ;
}
int main()
{
double num ;
//printf("Enter number:") ; scanf("%le",&num) ;
double diff=nextafter(num,INFINITY)-num ;
printf("f1=%le\n",f1(num)) ;
printf("f2=%le\n",f2(num)) ;
printf("difference=%le\n",diff) ;
if (num+f1(num)/2.0==num) printf("Equal1\n") ;
if (num+f2(num)/2.0==num) printf("Equal2\n") ;
return 0;
}`
Кажется, что моя функция f2() возвращает значение, равное моей вычисленной разнице с использованием функции nextafter(). Иногда функция F1()
возвращает значение, равное f2(), а иногда возвращает значение в 2 раза меньше, чем f2().
Но проверка условий «если» показывает, что значение, возвращаемое f1(), является фактической разницей между двумя последовательными числами типа double, потому что num+f1(num)/2.0==num и num+f2(num)/2.0!=num.
Так почему же иногда (например, для num=199.23999) f1()!=f2()? И что я делаю неправильно?
f1
не находит разницы между a и следующим представимым числом (в направлении большей величины). Он находит наименьшую степень двойки x такую, что сложение с плавающей запятой |a| и x не производит |a|.
Если а' является следующим представимым числом после а, то а'-а есть разница между ними, и добавление а'-а к а дает а'. Но это не означает, что добавление (а'-а)/2 к а не дает а'. Будет это или нет, зависит от округления.
В арифметике действительных чисел сумма (a'−a)/2 и a является средней точкой между a и a'. Правило округления по умолчанию состоит в том, чтобы округлить до ближайшего представимого значения, а при совпадении округлить до ближайшего представимого значения, которое имеет четную младшую цифру в значащем.
Рассмотрим три последовательных представимых числа 1+0•2−52, 1+1•2−52 и 1+2•2−52. Когда мы добавляем 2−53 к 1+0•2−52, арифметическая сумма действительных чисел равна 1+0,5•2−52, а два ближайших представимых значения равны 1+0•2−52 и 1+1•2−52. Из них первый имеет четную младшую цифру, поэтому в результате сложения с плавающей запятой получается именно он. Когда мы добавляем 2−53 к 1+1•2−52, арифметическая сумма действительных чисел равна 1+1,5•2−52, а два ближайших представимых значения равны 1+1•2−52 и 1+2•2−52. Из них последний имеет четную младшую цифру, поэтому в результате сложения с плавающей запятой получается именно он.
Это означает, что при вводе 1+0•2−52f1
дает 2−52, потому что добавление 2−52 к 1+0•2−52 дает 1+1•2−52, а добавление 2−53 — нет. А для ввода 1+1•2−52f1
дает 2−53, потому что добавление 2−53 дает 1+2•2−52 (а добавление 2−54 — нет).
Предположим (для простоты объяснения), у вас есть трехзначное десятичное число с плавающей запятой. Если прибавить
5.00e-3=0.00500
к1.01
и округлить результат до 3 цифр, получится1.02
, хотя разница между1.01
и1.02
в два раза больше.