rangeOfMatchingFloatingPointNumbers function
Determines the range of floating point numbers that will match the specified value with the given tolerance.
Implementation
Tuple2<double, double> rangeOfMatchingFloatingPointNumbers(
double value, int maxNumbersBetween) {
// Make sure ulpDifference is non-negative
if (maxNumbersBetween < 1) {
throw ArgumentError.value(
maxNumbersBetween, 'maxNumbersBetween', messages.argumentPositive);
}
// If the value is infinity (positive or negative) just
// return the same infinity for the range.
if (value.isInfinite) {
return Tuple2<double, double>(value, value);
}
// If the value is a NaN then the range is a NaN too.
if (value.isNaN) {
return Tuple2<double, double>(double.nan, double.nan);
}
// Translate the bit pattern of the double to an integer.
// Note that this leads to:
// double > 0 --> long > 0, growing as the double value grows
// double < 0 --> long < 0, increasing in absolute magnitude as the double
// gets closer to zero!
// i.e. 0 - double.epsilon will give the largest long value!
var bytes = ByteData(8);
bytes.setFloat64(0, value);
int intValue = bytes.getInt64(0);
// We need to protect against over- and under-flow of the intValue when
// we start to add the ulpsDifference.
if (intValue < 0) {
// Note that long.MinValue has the same bit pattern as
// -0.0. Therefore we're working in opposite direction (i.e. add if we want to
// go more negative and subtract if we want to go less negative)
var topRangeEnd = (int64MinValue - intValue).abs() < maxNumbersBetween
// Got underflow, which can be fixed by splitting the calculation into two bits
// first get the remainder of the intValue after subtracting it from the long.MinValue
// and add that to the ulpsDifference. That way we'll turn positive without underflow
? int64BitsToDouble(maxNumbersBetween + (int64MinValue - intValue))
// No problems here, move along.
: int64BitsToDouble(intValue - maxNumbersBetween);
var bottomRangeEnd = intValue.abs() < maxNumbersBetween
// Underflow, which means we'd have to go further than a long would allow us.
// Also we couldn't translate it back to a double, so we'll return -Double.MaxValue
? -double.maxFinite
// intValue is negative. Adding the positive ulpsDifference means that it gets less negative.
// However due to the conversion way this means that the actual double value gets more negative :-S
: int64BitsToDouble(intValue + maxNumbersBetween);
return Tuple2<double, double>(bottomRangeEnd, topRangeEnd);
} else {
// IntValue is positive
var topRangeEnd = int64MaxValue - intValue < maxNumbersBetween
// Overflow, which means we'd have to go further than a long would allow us.
// Also we couldn't translate it back to a double, so we'll return Double.MaxValue
? double.maxFinite
// No troubles here
: int64BitsToDouble(intValue + maxNumbersBetween);
// Check the bottom range end for underflows
var bottomRangeEnd = intValue > maxNumbersBetween
// No problems here. IntValue is larger than ulpsDifference so we'll end up with a
// positive number.
? int64BitsToDouble(intValue - maxNumbersBetween)
// Int value is bigger than zero but smaller than the ulpsDifference. So we'll need to deal with
// the reversal at the negative end
: int64BitsToDouble(int64MinValue + (maxNumbersBetween - intValue));
return Tuple2<double, double>(bottomRangeEnd, topRangeEnd);
}
}