OpenSCAD User Manual/General/Nearly Equal

Example code for using MIN_NORMAL and MAX.VALUE to correctly evaluate the difference between two floating point values that have a very small difference, or are themselves very small.

Two FP values that should have the same value but are calculated in different ways may have small differences in their encodings when their differences are close to, or less than, the Min.Normal value derived from the numerical representation. The epsilon value may be set to a value of difference acceptable to the application using nearlyEqual() when accuracy is relaxed from the theoretical best values of MIN_NORMAL and MAX.VALUE

public static boolean nearlyEqual(float a, float b, float epsilon)
    {
	final float absA = Math.abs(a);
	final float absB = Math.abs(b);
	final float diff = Math.abs(a - b);

	if (a == b) { // shortcut, handles infinities
		return true;
    } else if (a == 0 || b == 0 || (absA + absB < Float.MIN_NORMAL))
        {
		// a or b is zero or both are extremely close to it
		// relative error is less meaningful here
		return diff < (epsilon * Float.MIN_NORMAL);
		} else { // use relative error
			return diff / Math.min((absA + absB), Float.MAX_VALUE) < epsilon;
		}
	}

The following SCAD function returns true when two floating point values are within epsilon's value of each other. This form of comparison takes account of the limitations of representing floating point values as binary encoded values. The Min_Normal value is derived from the 64 bit values used in OpenSCAD.

function nearly_equal( a, b, epsilon=0.00001 ) =
	let(absA = abs(a),
		absB = abs(b),
		diff = abs(a - b)
		)
	a == b ? 
		true // shortcut, handles infinities
	: a == 0 || b == 0 || (absA + absB < Min_Normal ) ?
		// when a or b are zero or both are extremely close to it
		// relative error is less meaningful here
		diff < (epsilon * Min_Normal)
	: diff / min((absA + absB), MAX_VALUE) < epsilon
	;


echo( "testing _nearly_equal()" );
echo( _neq = _nearly_equal( 0.01000000000001, 0.01000000000001) );
echo( _neq = _nearly_equal( 0.01000000000009, 0.01000000000001) );
echo( _neq1= _nearly_equal( 0.010001,         0.010001) );
echo( _neq3= _nearly_equal( 0.010003,         0.010001) );
echo( _neq9= _nearly_equal( 0.010009,         0.010001) );

echo( "testing nearly_equal()" );
echo( neq=  nearly_equal( 0.01000001, 0.01000001 ) );
echo( neq=  nearly_equal( 0.0100001,  0.01000001 ) );
echo( neq=  nearly_equal( 0.010001,   0.01000001 ) );

assert(   _nearly_equal( 0.01000000000000001,  0.01000000000000001) );
assert(   _nearly_equal( 0.010001,         0.010001) );
assert( ! _nearly_equal( 0.010003,         0.010001) );
assert( ! _nearly_equal( 0.010009,         0.010001) );

assert(    nearly_equal( 0.01000000000000001,  0.01000000000000001) );
assert(    nearly_equal( 0.0100001,  0.01000001 ) );
assert( !  nearly_equal( 0.010001,   0.01000001 ) );