Is there an optimized, performant way to round a double to the exact value nearest multiple of a given power of two fraction?
In other words, round .44 to the nearest 1/16 (in other words, to a value that can be expressed as n/16 where n is an integer) would be .4375. Note: this is relevant because power of two fractions can be stored without rounding errors, e.g.
public class PowerOfTwo {
public static void main(String... args) {
double inexact = .44;
double exact = .4375;
System.out.println(inexact + ": " + Long.toBinaryString(Double.doubleToLongBits(inexact)));
System.out.println(exact + ": " + Long.toBinaryString(Double.doubleToLongBits(exact)));
}
}
Output:
0.44: 11111111011100001010001111010111000010100011110101110000101001
0.4375: 11111111011100000000000000000000000000000000000000000000000000
If you want to chose the power of two, the simplest way is to multiply by e.g. 16, round to nearest integer, then divide by 16. Note that division by a power of two is exact if the result is a normal number. It can cause rounding error for subnormal numbers.
Here is a sample program using this technique:
public class Test {
public static void main(String[] args) {
System.out.println(roundToPowerOfTwo(0.44, 2));
System.out.println(roundToPowerOfTwo(0.44, 3));
System.out.println(roundToPowerOfTwo(0.44, 4));
System.out.println(roundToPowerOfTwo(0.44, 5));
System.out.println(roundToPowerOfTwo(0.44, 6));
System.out.println(roundToPowerOfTwo(0.44, 7));
System.out.println(roundToPowerOfTwo(0.44, 8));
}
public static double roundToPowerOfTwo(double in, int power) {
double multiplier = 1 << power;
return Math.rint(in * multiplier) / multiplier;
}
}
Output:
0.5
0.5
0.4375
0.4375
0.4375
0.4375
0.44140625
If the question is about rounding any number to a pre-determined binary precision, what you need to do is this:
Convert the value to long using 'Double.doubleToLongBits()`
Examine the exponent: if it's too big (exponent+required precision>51, the number of bits in the significand), you won't be able to do any rounding but you won't have to: the number already satisfies your criteria.
If on the other hand exponent+required precision<0, the result of the rounding is always 0.
In any other case, look at the significand and blot out all the bits that are below the exponent+required precisionth significant bit.
Convert the number back to double using Double.longBitsToDouble()
Getting this right in all corner cases is a bit tricky. If I have to solve such a task, I'd usually start with a naive implementation that I can be pretty sure will be correct and only then start implementing an optimized version. While doing so, I can always compare against the naive approach to validate my results.
The naive approach is to start with 1 and multiply / divide it with / by 2 until we have bracketed the absolute value of the input. Then, we'll output the nearer of the boundaries. It's actually a bit more complicated: If the value is a NaN or infinity, it requires special treatment.
Here is the code:
public static double getClosestPowerOf2Loop(final double x) {
final double absx = Math.abs(x);
double prev = 1.0;
double next = 1.0;
if (Double.isInfinite(x) || Double.isNaN(x)) {
return x;
} else if (absx < 1.0) {
do {
prev = next;
next /= 2.0;
} while (next > absx);
} else if (absx > 1.0) {
do {
prev = next;
next *= 2.0;
} while (next < absx);
}
if (x < 0.0) {
prev = -prev;
next = -next;
}
return (Math.abs(next - x) < Math.abs(prev - x)) ? next : prev;
}
I hope the code will be clear without further explanation. Since Java 8, you can use !Double.isFinite(x) as a replacement for Double.isInfinite(x) || Double.isNaN(x).
Let's see for an optimized version. As other answers have already suggested, we should probably look at the bit representation. Java requires floating point values to be represented using IEE 754. In that format, numbers in double (64 bit) precision are represented as
1 bit sign,
11 bits exponent and
52 bits mantissa.
We will special-case NaNs and infinities (which are represented by special bit patterns) again. However, there is yet another exception: The most significant bit of the mantissa is implicitly 1 and not found in the bit pattern – except for very small numbers where a so-called subnormal representation us used where the most significant digit is not the most significant bit of the mantissa. Therefore, for normal numbers we will simply set the mantissa's bits to all 0 but for subnormals, we convert it to a number where none but the most significant 1 bit is preserved. This procedure always rounds towards zero so to get the other bound, we simply multiply by 2.
Let's see how this all works together:
public static double getClosestPowerOf2Bits(final double x) {
if (Double.isInfinite(x) || Double.isNaN(x)) {
return x;
} else {
final long bits = Double.doubleToLongBits(x);
final long signexp = bits & 0xfff0000000000000L;
final long mantissa = bits & 0x000fffffffffffffL;
final long mantissaPrev = Math.abs(x) < Double.MIN_NORMAL
? Long.highestOneBit(mantissa)
: 0x0000000000000000L;
final double prev = Double.longBitsToDouble(signexp | mantissaPrev);
final double next = 2.0 * prev;
return (Math.abs(next - x) < Math.abs(prev - x)) ? next : prev;
}
}
I'm note entirely sure I have covered all corner cases but the following tests do run:
public static void main(final String[] args) {
final double[] values = {
5.0, 4.1, 3.9, 1.0, 0.0, -0.1, -8.0, -8.1, -7.9,
0.9 * Double.MIN_NORMAL, -0.9 * Double.MIN_NORMAL,
Double.NaN, Double.MAX_VALUE, Double.MIN_VALUE,
Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY,
};
for (final double value : values) {
final double powerL = getClosestPowerOf2Loop(value);
final double powerB = getClosestPowerOf2Bits(value);
System.out.printf("%17.10g --> %17.10g %17.10g%n",
value, powerL, powerB);
assert Double.doubleToLongBits(powerL) == Double.doubleToLongBits(powerB);
}
}
Output:
5.000000000 --> 4.000000000 4.000000000
4.100000000 --> 4.000000000 4.000000000
3.900000000 --> 4.000000000 4.000000000
1.000000000 --> 1.000000000 1.000000000
0.000000000 --> 0.000000000 0.000000000
-0.1000000000 --> -0.1250000000 -0.1250000000
-8.000000000 --> -8.000000000 -8.000000000
-8.100000000 --> -8.000000000 -8.000000000
-7.900000000 --> -8.000000000 -8.000000000
2.002566473e-308 --> 2.225073859e-308 2.225073859e-308
-2.002566473e-308 --> -2.225073859e-308 -2.225073859e-308
NaN --> NaN NaN
1.797693135e+308 --> 8.988465674e+307 8.988465674e+307
4.900000000e-324 --> 4.900000000e-324 4.900000000e-324
-Infinity --> -Infinity -Infinity
Infinity --> Infinity Infinity
How about performance?
I have run the following benchmark
public static void main(final String[] args) {
final Random rand = new Random();
for (int i = 0; i < 1000000; ++i) {
final double value = Double.longBitsToDouble(rand.nextLong());
final double power = getClosestPowerOf2(value);
}
}
where getClosestPowerOf2 is to be replaced by either getClosestPowerOf2Loop or getClosestPowerOf2Bits. On my laptop, I get the following results:
getClosestPowerOf2Loop: 2.35 s
getClosestPowerOf2Bits: 1.80 s
Was that really worth the effort?
You are going to need some bit magic if you are going to round to arbitrary powers of 2.
You will need to inspect the exponent:
int exponent = Math.getExponent(inexact);
Then knowing that there are 53 bits in the mantissa can find the bit at which you need to round with.
Or just do:
Math.round(inexact* (1l<<exponent))/(1l<<exponent)
I use Math.round because I expect it to be optimal for the task as opposed to trying to implement it yourself.
Here is my first attempt at a solution, that doesn't handle all the cases in #biziclop's answer, and probably does "floor" instead of "round"
public static double round(double d, int precision) {
double longPart = Math.rint(d);
double decimalOnly = d - longPart;
long bits = Double.doubleToLongBits(decimalOnly);
long mask = -1l << (54 - precision);
return Double.longBitsToDouble(bits & mask) + longPart;
}
I came across this post trying to solve a related problem: how to efficiently find the two powers of two that bracket any given regular real value. Since my program deals in many types beside doubles I needed a general solution. Someone wanting to round to the nearest power of two can get the bracketing values and choose the closest. In my case the general solution required BigDecimals. Here is the trick I used.
For numbers > 1:
int exponent = myBigDecimal.toBigInteger.bitLength() - 1;
BigDecimal lowerBound = TWO.pow(exponent);
BigDecimal upperBound = TWO.pow(exponent+1);
For numbers > 0 and < 1:
int exponent = -(BigDecimal.ONE.divide(myBigDecimal, myContext).toBigInteger().bitLength()-1);
BigDecimal lowerBound = TWO.pow(exponent-1);
BigDecimal upperBound = TWO.pow(exponent);
I have only lined out the positive case. You generally take a number, and use this algorithm on it's absolute value. And then if in the original problem the number was negative you multiply the algorithm's result by -1. Finally the orignal num == 0 or num == 1 are trivial to handle outside this algorithm. That covers the whole real number line except infinties and nans which you deal with before calling this algorithm.
Related
I tried to calculate a series of the N first fibonacci numbers using Binets Formula.
Every result i get is correct until F47 where the result is NEGATIVE.
This is my result : -1323752223
And heres the expected result : 2971215073
I really think the problem occures during the double to int conversion
Source Code:
import java.lang.Math;
class fibonacci{
public static int NthFibonacci(int n){
double fi = 1.61803398875;
int fb = (int)Math.round((Math.pow(fi,n) - Math.pow(1-fi,n))/Math.sqrt(5));
return fb;
}
public static void FibonacciSeries(Integer n){
for(int i = 0; i < n; i++){
System.out.println(NthFibonacci(i) + " ");
}
}
public static void main(String[] args) {
FibonacciSeries(50);
}
}
The real explanation for the behavior of the version in your question giving a negative number is a bit subtle.
At F47, this expression
(Math.pow(fi, n) - Math.pow(1 - fi, n)) / Math.sqrt(5)
will give you 2.971215073009069E9 ... which is close to the desired 2971215073.
The problem arises when you call Math.round(2.971215073009069E9). This returns a long - 2971215073L. But then you cast the result of the round call to an int, and it all goes pear-shaped.
Casting a long to an int will just lop off the top 32 bits ... and that results in a meaningless number.
If we modify fibonacci to return a long instead of an int, we get correct results up to F55. F56 and F57 are off by 1. F58 is off by 2.
What is happening now is that we are running into the problem that double (64-bit IEEE floating point) has only about 13.5 decimal digits of precision. The rounding error incurred in the computation of the intermediate floating point value for F56 larger that 0.5 ... so the rounded value is then incorrect.
The computed fibonacci numbers continue to get increasingly inaccurate until you get to F93, where the (modified) fibonacci method returns Long.MAX_VALUE.
To get correct values for very large Fibonacci numbers:
we need to use BigInteger to represent the numbers,
we need to do the computations using BigDecimal with sufficient precision, and (maybe)
we need to use a more accurate value for phi.
Or we need to use the recurrence relationship to compute the numbers.
The 2 take-aways from all of this are:
casting a long to an int is a lossy conversion, and
floating point arithmetic is inexact and ... tricky.
I think that the problem does not have something to do with the double conversion.
int can store numbers that can be represented by 32 bits. This means the highest number integer can represents is 2.147.483.647.
The F47 is breaking this limit and results in an bit-overflow, so it starts at -2.147.483.68 and adds the rest of your 2971215073 - 2147483647 to it. -1323752223 is the outcome.
Use a long (64bit) instead of an int and you should be good :)
2971215073 is too big to be represented as an int at all. The maximum value of an int -- Integer.MAX_VALUE -- is 2^31 - 1, or 2147483647.
Ok so i found a decent fix.
I used a Geometrical version of Binets rule which you can find here : Binets Geometrical Rule
I also used long instead of int so now I can accurately calculate up to F70. F71 is wrong by a digit and after that it just builds up.
New Source Code :
import java.lang.Math;
class fibonacci{
public static long NthFibonacci(int n){
double a = (1/Math.sqrt(5))*Math.pow(2, n);
double radians1 = Math.toRadians(36.0);
double radians2 = Math.toRadians(108.0);
double b = Math.pow(Math.cos(radians1), n) - Math.pow(Math.cos(radians2), n);
long fb = (long) Math.round(a*b);
return fb;
}
public static void FibonacciSeries(int n){
for(int i = 0; i < n; i++){
System.out.println( i + " : " + NthFibonacci(i));
}
}
public static void main(String[] args) {
FibonacciSeries(100);
}
}
I am using BigDecimal for division. I would like the quotient to be rounded to the correct number of significant figures.
For example
#Test
public void testBigDDivision() {
BigDecimal top = new BigDecimal("0.25");
BigDecimal bottom = new BigDecimal("105");
int topSigFig = significantDigits(top);
int botSigFig = significantDigits(bottom);
// evaluates to 2 in this example
int scale = (topSigFig > botSigFig) ? botSigFig : topSigFig;
BigDecimal quot = top.divide(bottom, scale, RoundingMode.HALF_UP);
BigDecimal expected = new BigDecimal("0.0024");
Assert.assertTrue(String.format("Got %s; Expected %s", quot, expected),
expected.compareTo(quot) == 0); // fails "Got 0.00; Expected 0.0024"
}
// stolen from https://stackoverflow.com/a/21443880
public static int significantDigits(BigDecimal input) {
input = input.stripTrailingZeros();
return input.scale() < 0
? input.precision() - input.scale()
: input.precision();
}
What is the correct way to programmatically determine the scale to ensure the quotient has the correct number of significant figures?
Significant figures are situational, not computable. As you mentioned in the comment, you're doing a program to recalculate the percentage of a solution with several ingredients. I suggest you transform the ingredients' units until you have no significant digits to the right of the decimal point in the input, then do the calculation.
For this you need to know the unit in the input. So, if the test input is in "grams", you'd first transform to milligrams (grams*1000).
So numbers would be 250 and 105000; then do the division and keep 2 or 3 decimal digits - less than that doesn't usually make sense when the input has no decimal numbers.
I have the following code :
Double x = 17.0;
Double y = 0.1;
double remainder = x.doubleValue() % y.doubleValue();
When I run this I get remainder = 0.09999999999999906
Any idea why??
I basically need to check that x is fully divisible by y. Can you suggest alternative ways to do that in java.
Thanks
Because of how floating-point numbers are represented.
If you want exact values, use BigDecimal:
BigDecimal remainder = BigDecimal.valueOf(x).remainder(BigDecimal.valueOf(y));
Another way to to that is to multiple each value by 10 (or 100, 1000), cast to int, and then use %.
You need to compare your result which allows for rounding error.
if (remainder < ERROR || remainder > 0.1 - ERROR)
Also, don't use Double when you mean to use double
Expecting precise results from double arithmetic is problematic on computers. The basic culprit is that us humans use base 10 mostly, whereas computers normally store numbers in base 2. There are conversion problems between the two.
This code will do what you want:
public static void main(String[] args) {
BigDecimal x = BigDecimal.valueOf(17.0);
BigDecimal y = BigDecimal.valueOf(0.1);
BigDecimal remainder = x.remainder(y);
System.out.println("remainder = " + remainder);
final boolean divisible = remainder.equals(BigDecimal.valueOf(0.0));
System.out.println("divisible = " + divisible);
}
In Java the floating point arithmetic is not represented precisely. For example this java code:
float a = 1.2;
float b= 3.0;
float c = a * b;
if(c == 3.6){
System.out.println("c is 3.6");
}
else {
System.out.println("c is not 3.6");
}
Prints "c is not 3.6".
I'm not interested in precision beyond 3 decimals (#.###). How can I deal with this problem to multiply floats and compare them reliably?
It's a general rule that floating point number should never be compared like (a==b), but rather like (Math.abs(a-b) < delta) where delta is a small number.
A floating point value having fixed number of digits in decimal form does not necessary have fixed number of digits in binary form.
Addition for clarity:
Though strict == comparison of floating point numbers has very little practical sense, the strict < and > comparison, on the contrary, is a valid use case (example - logic triggering when certain value exceeds threshold: (val > threshold) && panic();)
If you are interested in fixed precision numbers, you should be using a fixed precision type like BigDecimal, not an inherently approximate (though high precision) type like float. There are numerous similar questions on Stack Overflow that go into this in more detail, across many languages.
I think it has nothing to do with Java, it happens on any IEEE 754 floating point number. It is because of the nature of floating point representation. Any languages that use the IEEE 754 format will encounter the same problem.
As suggested by David above, you should use the method abs of java.lang.Math class to get the absolute value (drop the positive/negative sign).
You can read this: http://en.wikipedia.org/wiki/IEEE_754_revision and also a good numerical methods text book will address the problem sufficiently.
public static void main(String[] args) {
float a = 1.2f;
float b = 3.0f;
float c = a * b;
final float PRECISION_LEVEL = 0.001f;
if(Math.abs(c - 3.6f) < PRECISION_LEVEL) {
System.out.println("c is 3.6");
} else {
System.out.println("c is not 3.6");
}
}
I’m using this bit of code in unit tests to compare if the outcome of 2 different calculations are the same, barring floating point math errors.
It works by looking at the binary representation of the floating point number. Most of the complication is due to the fact that the sign of floating point numbers is not two’s complement. After compensating for that it basically comes down to just a simple subtraction to get the difference in ULPs (explained in the comment below).
/**
* Compare two floating points for equality within a margin of error.
*
* This can be used to compensate for inequality caused by accumulated
* floating point math errors.
*
* The error margin is specified in ULPs (units of least precision).
* A one-ULP difference means there are no representable floats in between.
* E.g. 0f and 1.4e-45f are one ULP apart. So are -6.1340704f and -6.13407f.
* Depending on the number of calculations involved, typically a margin of
* 1-5 ULPs should be enough.
*
* #param expected The expected value.
* #param actual The actual value.
* #param maxUlps The maximum difference in ULPs.
* #return Whether they are equal or not.
*/
public static boolean compareFloatEquals(float expected, float actual, int maxUlps) {
int expectedBits = Float.floatToIntBits(expected) < 0 ? 0x80000000 - Float.floatToIntBits(expected) : Float.floatToIntBits(expected);
int actualBits = Float.floatToIntBits(actual) < 0 ? 0x80000000 - Float.floatToIntBits(actual) : Float.floatToIntBits(actual);
int difference = expectedBits > actualBits ? expectedBits - actualBits : actualBits - expectedBits;
return !Float.isNaN(expected) && !Float.isNaN(actual) && difference <= maxUlps;
}
Here is a version for double precision floats:
/**
* Compare two double precision floats for equality within a margin of error.
*
* #param expected The expected value.
* #param actual The actual value.
* #param maxUlps The maximum difference in ULPs.
* #return Whether they are equal or not.
* #see Utils#compareFloatEquals(float, float, int)
*/
public static boolean compareDoubleEquals(double expected, double actual, long maxUlps) {
long expectedBits = Double.doubleToLongBits(expected) < 0 ? 0x8000000000000000L - Double.doubleToLongBits(expected) : Double.doubleToLongBits(expected);
long actualBits = Double.doubleToLongBits(actual) < 0 ? 0x8000000000000000L - Double.doubleToLongBits(actual) : Double.doubleToLongBits(actual);
long difference = expectedBits > actualBits ? expectedBits - actualBits : actualBits - expectedBits;
return !Double.isNaN(expected) && !Double.isNaN(actual) && difference <= maxUlps;
}
This is a weakness of all floating point representations, and it happens because some numbers that appear to have a fixed number of decimals in the decimal system, actually have an infinite number of decimals in the binary system. And so what you think is 1.2 is actually something like 1.199999999997 because when representing it in binary it has to chop off the decimals after a certain number, and you lose some precision. Then multiplying it by 3 actually gives 3.5999999...
http://docs.python.org/py3k/tutorial/floatingpoint.html <- this might explain it better (even if it's for python, it's a common problem of the floating point representation)
Like the others wrote:
Compare floats with: if (Math.abs(a - b) < delta)
You can write a nice method for doing this:
public static int compareFloats(float f1, float f2, float delta)
{
if (Math.abs(f1 - f2) < delta)
{
return 0;
} else
{
if (f1 < f2)
{
return -1;
} else {
return 1;
}
}
}
/**
* Uses <code>0.001f</code> for delta.
*/
public static int compareFloats(float f1, float f2)
{
return compareFloats(f1, f2, 0.001f);
}
So, you can use it like this:
if (compareFloats(a * b, 3.6f) == 0)
{
System.out.println("They are equal");
}
else
{
System.out.println("They aren't equal");
}
There is an apache class for comparing doubles: org.apache.commons.math3.util.Precision
It contains some interesting constants: SAFE_MIN and EPSILON, which are the maximum possible deviations when performing arithmetic operations.
It also provides the necessary methods to compare, equal or round doubles.
Rounding is a bad idea. Use BigDecimal and set it's precision as needed.
Like:
public static void main(String... args) {
float a = 1.2f;
float b = 3.0f;
float c = a * b;
BigDecimal a2 = BigDecimal.valueOf(a);
BigDecimal b2 = BigDecimal.valueOf(b);
BigDecimal c2 = a2.multiply(b2);
BigDecimal a3 = a2.setScale(2, RoundingMode.HALF_UP);
BigDecimal b3 = b2.setScale(2, RoundingMode.HALF_UP);
BigDecimal c3 = a3.multiply(b3);
BigDecimal c4 = a3.multiply(b3).setScale(2, RoundingMode.HALF_UP);
System.out.println(c); // 3.6000001
System.out.println(c2); // 3.60000014305114740
System.out.println(c3); // 3.6000
System.out.println(c == 3.6f); // false
System.out.println(Float.compare(c, 3.6f) == 0); // false
System.out.println(c2.compareTo(BigDecimal.valueOf(3.6f)) == 0); // false
System.out.println(c3.compareTo(BigDecimal.valueOf(3.6f)) == 0); // false
System.out.println(c3.compareTo(BigDecimal.valueOf(3.6f).setScale(2, RoundingMode.HALF_UP)) == 0); // true
System.out.println(c3.compareTo(BigDecimal.valueOf(3.6f).setScale(9, RoundingMode.HALF_UP)) == 0); // false
System.out.println(c4.compareTo(BigDecimal.valueOf(3.6f).setScale(2, RoundingMode.HALF_UP)) == 0); // true
}
To compare two floats, f1 and f2 within precision of #.### I believe you would need to do like this:
((int) (f1 * 1000 + 0.5)) == ((int) (f2 * 1000 + 0.5))
f1 * 1000 lifts 3.14159265... to 3141.59265, + 0.5 results in 3142.09265 and the (int) chops off the decimals, 3142. That is, it includes 3 decimals and rounds the last digit properly.
Currently i have this method:
static boolean checkDecimalPlaces(double d, int decimalPlaces){
if (d==0) return true;
double multiplier = Math.pow(10, decimalPlaces);
double check = d * multiplier;
check = Math.round(check);
check = check/multiplier;
return (d==check);
}
But this method fails for checkDecmialPlaces(649632196443.4279, 4) probably because I do base 10 math on a base 2 number.
So how can this check be done correctly?
I thought of getting a string representation of the double value and then check that with a regexp - but that felt weird.
EDIT:
Thanks for all the answers. There are cases where I really get a double and for those cases I implemented the following:
private static boolean checkDecimalPlaces(double d, int decimalPlaces) {
if (d == 0) return true;
final double epsilon = Math.pow(10.0, ((decimalPlaces + 1) * -1));
double multiplier = Math.pow(10, decimalPlaces);
double check = d * multiplier;
long checkLong = (long) Math.abs(check);
check = checkLong / multiplier;
double e = Math.abs(d - check);
return e < epsilon;
}
I changed the round to a truncation. Seems that the computation done in round increases the inaccuracy too much. At least in the failing testcase.
As some of you pointed out if I could get to the 'real' string input I should use BigDecimal to check and so I have done:
BigDecimal decimal = new BigDecimal(value);
BigDecimal checkDecimal = decimal.movePointRight(decimalPlaces);
return checkDecimal.scale() == 0;
The double value I get comes from the Apache POI API that reads excel files. I did a few tests and found out that although the API returns double values for numeric cells I can get a accurate representation when I immediately format that double with the DecimalFormat:
DecimalFormat decimalFormat = new DecimalFormat();
decimalFormat.setMaximumIntegerDigits(Integer.MAX_VALUE);
// don't use grouping for numeric-type cells
decimalFormat.setGroupingUsed(false);
decimalFormat.setDecimalFormatSymbols(new DecimalFormatSymbols(Locale.US));
value = decimalFormat.format(numericValue);
This also works for values that can't be represented exactly in binary format.
The test fails, because you have reached the accuracy of the binary floating point representation, which is approximately 16 digits with IEEE754 double precision. Multiplying by 649632196443.4279 by 10000 will truncate the binary representation, leading to errors when rounding and dividing afterwards, thereby invalidating the result of your function completely.
For more details see http://en.wikipedia.org/wiki/Floating_point#Accuracy_problems
A better way would be to check whether the n+1 decimal places are below a certain threshold. If d - round(d) is less than epsilon (see limit), the decimal representation of d has no significant decimal places. Similarly if (d - round(d)) * 10^n is less than epsilon, d can have at most n significant places.
Use Jon Skeet's DoubleConverter to check for the cases where d isn't accurate enough to hold the decimal places you are looking for.
If your goal is to represent a number with exactly n significant figures to the right of the decimal, BigDecimal is the class to use.
Immutable, arbitrary-precision signed
decimal numbers. A BigDecimal consists
of an arbitrary precision integer
unscaled value and a 32-bit integer
scale. If zero or positive, the scale
is the number of digits to the right
of the decimal point. If negative, the
unscaled value of the number is
multiplied by ten to the power of the
negation of the scale. The value of
the number represented by the
BigDecimal is therefore (unscaledValue
× 10-scale).
scale can be set via setScale(int)
As with all floating point arithmetic, you should not check for equality, but rather that the error (epsilon) is sufficiently small.
If you replace:
return (d==check);
with something like
return (Math.abs(d-check) <= 0.0000001);
it should work. Obviously, the epsilon should be selected to be small enough compared with the number of decimals you're checking for.
The double type is a binary floating point number. There are always apparent inaccuracies in dealing with them as if they were decimal floating point numbers. I don't know that you'll ever be able to write your function so that it works the way you want.
You will likely have to go back to the original source of the number (a string input perhaps) and keep the decimal representation if it is important to you.
If you can switch to BigDecimal, then as Ken G explains, that's what you should be using.
If not, then you have to deal with a host of issues as mentioned in the other answers. To me, you are dealing with a binary number (double) and asking a question about a decimal representation of that number; i.e., you are asking about a String. I think your intuition is correct.
I think this is better
Convert to string and interrogate the value for the exponent
public int calcBase10Exponet (Number increment)
{
//toSting of 0.0=0.0
//toSting of 1.0=1.0
//toSting of 10.0=10.0
//toSting of 100.0=100.0
//toSting of 1000.0=1000.0
//toSting of 10000.0=10000.0
//toSting of 100000.0=100000.0
//toSting of 1000000.0=1000000.0
//toSting of 1.0E7=1.0E7
//toSting of 1.0E8=1.0E8
//toSting of 1.0E9=1.0E9
//toSting of 1.0E10=1.0E10
//toSting of 1.0E11=1.0E11
//toSting of 0.1=0.1
//toSting of 0.01=0.01
//toSting of 0.0010=0.0010 <== need to trim off this extra zero
//toSting of 1.0E-4=1.0E-4
//toSting of 1.0E-5=1.0E-5
//toSting of 1.0E-6=1.0E-6
//toSting of 1.0E-7=1.0E-7
//toSting of 1.0E-8=1.0E-8
//toSting of 1.0E-9=1.0E-9
//toSting of 1.0E-10=1.0E-10
//toSting of 1.0E-11=1.0E-11
double dbl = increment.doubleValue ();
String str = Double.toString (dbl);
// System.out.println ("NumberBoxDefaultPatternCalculator: toSting of " + dbl + "=" + str);
if (str.contains ("E"))
{
return Integer.parseInt (str.substring (str.indexOf ("E") + 1));
}
if (str.endsWith (".0"))
{
return str.length () - 3;
}
while (str.endsWith ("0"))
{
str = str.substring (0, str.length () - 1);
}
return - (str.length () - str.indexOf (".") - 1);
}
Maybe it will be helpful. Here is my method
private int checkPrecisionOfDouble(Double atribute) {
String s = String.valueOf(atribute);
String[] split = s.split("\\.");
return split[1].length();
}
or
private boolean checkPrecisionOfDouble(Double atribute, int decimalPlaces) {
String s = String.valueOf(atribute);
String[] split = s.split("\\.");
return split[1].length() == decimalPlaces;
}
I'm not sure that this is really doable in general. For example, how many decimal places does 1.0e-13 have? What if it resulted from some rounding error while doing arithmetic and really is just 0 in disguise? If on, the other hand you are asking if there are any non-zero digits in the first n decimal places you can do something like:
static boolean checkDecimalPlaces(double d, unsigned int decimalPlaces){
// take advantage of truncation, may need to use BigInt here
// depending on your range
double d_abs = Math.abs(d);
unsigned long d_i = d_abs;
unsigned long e = (d_abs - d_i) * Math.pow(10, decimalPlaces);
return e > 0;
}