Kaleb Blue
Kaleb Blue

Reputation: 507

Efficient way of finding the number of decimals a double value has

I want to find an effiecient way of making sure that the number of decimal places in double is not more than three.

double num1 = 10.012; //True
double num2 = 10.2211; //False
double num2 = 10.2; //True

Currently, what I do is just use a regex split and count index of . like below.

String[] split = new Double(num).toString().split("\\.")
split[0].length() //num of decimal places

Is there an efficient or better way to do this since I'll be calling this function a lot?

Upvotes: 1

Views: 146

Answers (1)

T.J. Crowder
T.J. Crowder

Reputation: 1074505

If you want a solution that will tell you that information in a way that will agree with the eventual result of converting the double to a string, then efficiency doesn't really come into it; you basically have to convert to string and check. The result is that it's entirely possible for a double to contain a value that mathematically has a (say) non-zero value in (say) the hundred-thousandth place, but which when converted to string will not. Such is the joy of IEEE-754 double-precision binary floating point: The number of digits you get from the string representation is only as many as necessary to distinguish the value from its adjacent representable value. From the Double docs:

How many digits must be printed for the fractional part of m or a? There must be at least one digit to represent the fractional part, and beyond that as many, but only as many, more digits as are needed to uniquely distinguish the argument value from adjacent values of type double. That is, suppose that x is the exact mathematical value represented by the decimal representation produced by this method for a finite nonzero argument d. Then d must be the double value nearest to x; or if two double values are equally close to x, then d must be one of them and the least significant bit of the significand of d must be 0.

But if you're not concerned about that, and assuming limiting your value range to long is okay, you can do something like this:

private static boolean onlyThreePlaces(double v) {
    double d = (double)((long)(v * 1000)) / 1000;
    return d == v;
}

...which should have less memory overhead than a String-round-trip.

However, I'd be surprised if there weren't a fair number of times when that method and the result of Double.toString(double) didn't match in terms of digits after the decimal, for the reasons given above.


In a comment on the question, you've said (when I asked about the value range):

Honestly I'm not sure. I'm dealing with prices; For starters, I'll assume 0-200K

Using double for financial values is usually not a good idea. If you don't want to use BigDecimal because of memory concerns, pick your precision and use int or long depending on your value range. For instance, if you only need to-the-penny precision, you'd use values multiplied by 100 (e.g., 2000 is Ⓠ20 [or whatever currency you're using, I'm using Ⓠ for quatloos]). If you need precision to thousanths of a penny (as your question suggests), then multiply by 100000 (e.g., 2000000 is Ⓠ20). If you need more precision, pick a larger multiplier. Even if you go to hundred-thousanths of a penny (muliplier: 10000000), with long you have a range of Ⓠ-922,337,203,685 to Ⓠ922,337,203,685.

This has the side-benefit that it makes this check easier: Just a straight %. If your multiplier is 10000000 (hundred-thousandths of a penny), it's just value % 10000 != 0 to identify invalid ones (or value % 10000 == 0 to identify valid ones).

long num1 = 100120000;  // 10.012   => true
                        // 100120000 % 10000 is 0 = valid
long num2 = 102211000;  // 10.2211  => false
                        // 102211000 % 10000 is 1000 = invalid
long num3 = 102000000;  // 10.2     => true
                        // 102000000 % 10000 is 0 = valid

Upvotes: 2

Related Questions