неточность с плавающей запятой при повторении

У меня есть функция, которая вычисляет точку на трехмерном расстоянии на основе значения в диапазоне [0, 1] . Проблема, с которой я сталкиваюсь, состоит в том, что двоичное число с плавающей запятой не может представлять точно 1.

Математическое выражение, которое оценивается в функции, способно вычислить значение для t=1.0 , но значение никогда не будет принято функцией, потому что оно проверяет, следует ли для диапазона перед вычислением.

 curves_error curves_bezier(curves_PointList* list, curves_Point* dest, curves_float t) { /* ... */ if (t  1) return curves_invalid_args; /* ... */ return curves_no_error; } 

Как я могу, с этой функцией, вычислить трехмерную точку при t=1.0 ? Некоторое время назад я кое-что слышал об ELLIPSIS что, по-моему, имело отношение к такой проблеме, но я не уверен.

Спасибо

EDIT : Хорошо, извините. Я предположил, что float не может представлять ровно 1, из-за проблемы, с которой я сталкиваюсь. Проблема может быть в том, что я делал итерацию следующим образом:

 for (t=0; t <= 1.0; t += 0.1) { curves_error error = curves_bezier(points, point, t); if (error != curves_no_error) printf("Error with t = %f.\n", t); else printf("t = %f is ok.\n", t); } 

 for (t=0; t <= 1.0; t += 0.1) { 

ваша проблема в том, что двоичное число с плавающей запятой не может точно представлять 0.1 .

Ближайшее 32-битное одноточечное число IEEE754 с плавающей запятой составляет 0,100000001490116119384765625 и ближайшую 64-битную двойную точность 0,1000000000000000055511151231257827021181583404541015625. Если арифметика выполняется строго с 32-битной точностью, результатом добавления 0.1f десять раз до 0 является

 1.00000011920928955078125 

Если промежуточные вычисления выполняются с большей точностью, чем float , это может привести к точному или даже немного меньшему числу.

Чтобы исправить вашу проблему, в этом случае вы можете использовать

 for(k = 0; k <= 10; ++k) { t = k*0.1; 

потому что 10 * 0.1f - ровно 1.0 .

Другим вариантом является использование небольшого допуска в функции curves_bezier ,

 if (t > 1 && t < 1 + epsilon) { t = 1; } 

для подходящего небольшого эпсилона, возможно, float epsilon = 1e-6; ,

двоичный номер с плавающей запятой не может представлять точно 1

Доказательство того, что это можно найти, можно найти здесь .

Наиболее точное представление = 1.0E0

Там могут быть проблемы с

  1. дробные числа, которые имели бы бесконечные дробные числа в радиусе 2
  2. числа, которые слишком малы, чтобы точно представлять без потери точности
  3. числа, которые слишком велики для представления без потери точности.

Но 1.0 ни один из них!

Однако 0.1 является проблемным случаем, нарушающим точку 1, посмотрите на это :

Наиболее точное представление = 1.00000001490116119384765625E-1

Поэтому, если вы добавите 0,1 десять раз, вы получите 1.00000001490116119384765625E-0 что больше 1.0 .

(примеры приведены в 32-разрядных числах с плавающей точкой с одиночной точностью IEEE754)

Возможное решение:

 int i; for (i=0; i <= 10; i++) { t=i/10.0; curves_error error = curves_bezier(points, point, t); if (error != curves_no_error) { printf("Error with t = %f.\n", t); } else { printf("t = %f is ok.\n", t); } } 

Таким образом, ошибка двоичного формата не суммируется!

( Примечание: я использовал лишние фигурные скобки для операторов if и else . Сделайте это, когда-нибудь вы поблагодарите себя.)

Сравнивая числа с плавающей запятой, вы должны проверить, достаточно ли они достаточно близки, по причинам, указанным в других ответах, что-то вроде:

 #define EPSILON 0.000001f #define FEQUAL(a,b) (fabs((a) - (b)) < EPSILON)