I am doing high precision scientific computations. In looking for the best representation of various effects, I keep coming up with reasons to want to get the next higher (
Yes, there is a way. In C#:
public static double getInc (double d)
{
// Check for special values
if (double.IsPositiveInfinity(d) || double.IsNegativeInfinity(d))
return d;
if (double.IsNaN(d))
return d;
// Translate the double into binary representation
ulong bits = (ulong)BitConverter.DoubleToInt64Bits(d);
// Mask out the mantissa bits
bits &= 0xfff0000000000000L;
// Reduce exponent by 52 bits, so subtract 52 from the mantissa.
// First check if number is great enough.
ulong testWithoutSign = bits & 0x7ff0000000000000L;
if (testWithoutSign > 0x0350000000000000L)
bits -= 0x0350000000000000L;
else
bits = 0x0000000000000001L;
return BitConverter.Int64BitsToDouble((long)bits);
}
The increase can be added and subtracted.
There are functions available for doing exactly that, but they can depend on what language you use. Two examples:
if you have access to a decent C99 math library, you can use nextafter (and its float and long double variants, nextafterf
and nextafterl
); or the nexttoward
family (which take a long double as second argument).
if you write Fortran, you have the nearest intrinsic available
If you can't access these directly from your language, you can also look at how they're implemented in freely available, such as this one.
In regards to the epsilon function, it is an estimation of how far off the approximation of a decimal value the binary double could be. That is because, for very large positive or negative decimal numbers or very small positive or negative decimal numbers, many of them map to the same binary representation as a double. Try some very, very large or very, very small decimal numbers, create doubles from them and then transform back to a decimal number. You will find that you will not get the same decimal number back, but the one that the double is closest to instead.
For values near (near relative to the vast range of decimal values doubles can represent) 1 or -1, epsilon will be zero or very, very small. For values that progressively head towards + or - infinity or zero, epsilon will start to grow. At values extremely close to zero or either infinity, epsilon will be very large because the available binary representations for decimal values in those ranges are very, very sparse.
Most languages have intrinsic or library functions for acquiring the next or previous single-precision (32-bit) and/or double-precision (64-bit) number.
For users of 32-bit and 64-bit floating point arithmetic, a sound understanding of the basic constructs is very useful for avoiding some hazards with them. The IEEE standard applies uniformly, but still leaves a number of details up to implementers. Hence, a platform universal solution based on bit manipulations of the machine word representations may problematic and may depend on issues such as endian and so on. Whilst understanding all the gory details of how it could or should work at the bit level may demonstrate intellectual prowess, it is still better to use an intrinsic or library solution that is tailored for each platform and has a universal API across supported platforms.
I noticed solutions for C# and C++. Here are some for Java:
Math.nextUp:
public static double nextUp(double d):
Special Cases:
Parameters:
Returns:
public static float nextUp(float f):
Special Cases:
Parameters:
Returns:
The next two are a bit more complex to use. However, a direction towards zero or towards either positive or negative infinity seem the more likely and useful uses. Another use is to see an intermediate value exists between two values. One can determine how many exist between two values with a loop and counter. Also, it seems they, along with the nextUp methods, might be useful for increment/decrement in for loops.
Math.nextAfter:
public static double nextAfter(double start, double direction)
Special cases:
Parameters:
Returns:
public static float nextAfter(float start, double direction)
Special cases:
Parameters:
Returns:
As Thorsten S. says, this can be done with the BitConverter
class, but his method assumes that the DoubleToInt64Bits
method returns the internal byte structure of the double
, which it does not. The integer returned by that method actually returns the number of representable doubles between 0 and yours. I.e. the smallest positive double is represented by 1, the next largest double is 2, etc. etc. Negative numbers start at long.MinValue
and go away from 0d.
So you can do something like this:
public static double NextDouble(double value) {
// Get the long representation of value:
var longRep = BitConverter.DoubleToInt64Bits(value);
long nextLong;
if (longRep >= 0) // number is positive, so increment to go "up"
nextLong = longRep + 1L;
else if (longRep == long.MinValue) // number is -0
nextLong = 1L;
else // number is negative, so decrement to go "up"
nextLong = longRep - 1L;
return BitConverter.Int64BitsToDouble(nextLong);
}
This doesn't deal with Infinity
and NaN,
but you can check for those and deal with them however you like, if you're worried about it.
I'm not sure I'm following your problem. Surely the IEEE standard is totally uniform? For example, look at this excerpt from the wikipedia article for double precision numbers.
3ff0 0000 0000 0000 = 1
3ff0 0000 0000 0001 = 1.0000000000000002, the next higher number > 1
3ff0 0000 0000 0002 = 1.0000000000000004
What's wrong with just incrementing the least significant bit, in a binary or hex representation?
As far as the special numbers go (infinity, NaN,etc.), they're well defined, and there aren't very many of them. The limits are similarly defined.
Since you've obviously looked into this, I expect I've got the wrong end of the stick. If this isn't sufficient for your problem, could you try and clarify what you're wanting to achieve? What is your aim here?