I am trying to find the slope of a Bezier curve using the difference quotient:
( f(x + h) - f(x) ) / h
In calculus we usually use limits where we assume h is infinitely small and we evaluate the formula to find f'(x) which is the derivative formula.
Here, I wanted to try a similar concept and make h equal to FLT_MIN or FLT_TRUE_MIN which is supposed to be the smallest positive float value, and add it to 't' to approximate adding the smallest number possible to 't'. However, when I run getApproxNormal(0.5f) I just get 0 or NaN instead of somewhere in the ballpark of what the normal vector ratio should be.
sf::vector2f is a simple class that defines a mathematical vector with two coordinates (x and y)
#include <cmath>
#include <float.h>
class Vec2f //sf::vector2f
{
public:
float x, y;
Vec2f() : x(0), y(0) {}
Vec2f(float X, float Y) : x(X), y(Y) {}
friend Vec2f operator- (const Vec2f& a)
{
return Vec2f(-a.x, -a.y);
}
friend Vec2f operator- (const Vec2f& left, const Vec2f& right)
{
return Vec2f(left.x - right.x, left.y - right.y);
}
friend Vec2f operator/ (const Vec2f& left, float right)
{
return Vec2f(left.x / right, left.y / right);
}
};
const Vec2f _p0(0, 0);
const Vec2f _p1(0, 100);
const Vec2f _p2(100, 100);
Vec2f getBezierPoint(float t) // float t is a value between 0 and 1 that
//determines the percentage of the curve to sample the point for
//example when t = 0.5 that's 50% between the start and the endpoint of
//the bezier curve
{
float x = powf(1 - t, 2) * _p0.x + 2 * (1 - t) * t * _p1.x + std::pow(t, 2) * _p2.x;
float y = powf(1 - t, 2) * _p0.y + 2 * (1 - t) * t * _p1.y + std::pow(t, 2) * _p2.y;
return Vec2f(x, y);
}
float getApproxNormal(float t)
{
Vec2f d = (getBezierPoint(t + FLT_MIN) // issue is that both calls
//return the same value even after adding FLT_MIN
- getBezierPoint(t)) / FLT_MIN;
return -(d.x / d.y); // returns the negative inverse of the derivative
}
void main()
{
float normal = getApproxNormal(0.5f); // return NaN
}
As far as I know, it's difficult for a float to represent a larger number without sacrificing the smaller digits causing data loss in the least significant digits. This must mean that the smallest number I can add to a larger number varies depending on the larger number. If this is the case, how can I find the smallest number that I can add to a given float value.
If we imagine floating point numbers are implemented as decimals with a 5 digit number and a 2 digit exponent then the smallest possible number would be 1.0000 * 10 -99 if we add this to 1.0000 * 10 0 then after rounding to our 5 digit number we still have 1.0000 * 10 0. Floating point numbers are essentially implemented in the same way just with binary mantissa and exponent rather than decimal ones.
Therefore adding FLT_MIN
to anything other than another very small number will likely round out to the same number. What you actually want to do is find the next nearest number, fortunately c++ provides functions to do this https://en.cppreference.com/w/cpp/numeric/math/nextafter:
getApproxNormal(float t)
{
float next = std::nextafter(t, FLT_MAX);
float diff = next - t;
Vec2f d = (getBezierPoint(next)
- getBezierPoint(t)) / diff;
return -(d.x / d.y); // returns the negative inverse of the derivative
}
Note that due to the limitations of floating point accuracy you might not get the exact answer you expect.