How to determine whether number is exactly representable in floating-point? - java

Since float numbers are base-2 numeral system then it's not possible to represent 0.24F directly as the same it's not possible to represent 1/3 in decimal system without recurring decimal period i.e. 1/3=0.3333... or 0.(3).
So the float number 0.24F when printed back to decimal representation is shown as 0.23 with a change due to rounding:
println(0.24F) => 0.23999999463558197021484375
while 0.25F can be shown directly:
println(0.25F) => 0.25
But how can I determine that a number is exactly representable?
isExactFloat(0.25F) ==> true
isExactFloat(0.24F) ==> false
Maybe Java API has already some function to do that?
UPD
Here is a code which shows float numbers in range [-4, 4] with their internal representation:
public class FloatDestructure {
public static void main(String[] args) {
BigDecimal dec = BigDecimal.valueOf(-4000L, 3);
BigDecimal incr = BigDecimal.valueOf(1L, 3);
for (int i = 0; i <= 8000; i++) {
double dbl = dec.doubleValue();
floatDestuct(dbl, dec);
dec = dec.add(incr);
}
}
static boolean isExactFloat(double d) { return d == (float) d; }
static void floatDestuct(double val, BigDecimal dec) {
float value = (float) val;
int bits = Float.floatToIntBits(value);
int sign = bits >>> 31;
int exp = (bits >>> 23 & ((1 << 8) - 1)) - ((1 << 7) - 1);
int mantissa = bits & ((1 << 23) - 1);
float backToFloat = Float.intBitsToFloat((sign << 31) | (exp + ((1 << 7) - 1)) << 23 | mantissa);
boolean exactFloat = isExactFloat(val);
boolean exactFloatStr = Double.toString(value).length() <= 7;
System.out.println(dec.toString() + " " + (double) val + " " + (double) value + " sign: " + sign + " exp: " + exp + " mantissa: " + mantissa + " " + Integer.toBinaryString(mantissa) + " " + (double) backToFloat + " " + exactFloat + " " + exactFloatStr);
}
}
When mantissa is zero then the float is definitely exact. But in other cases like -0.375 or -1.625 it's not so clear.

In general, this is not possible.
As soon as the number is converted to a float or double, it is just an approximation of the number. So your input to isexactfloat() would not be exact...
If you have the exact version of floating point number in e.g. string format, then it would be possible to devise a function that could tell you if the float or double exactly represents the string formatted number or not. See the comment below by Carlos Heurberger on how to implement such a function.

I would like to share this function here.
// Determine whether number is exactly representable in double.
// i.e., No rounding to an approximation during the conversion.
// Results are valid for numbers in the range [2^-24, 2^52].
public static boolean isExactFloat(double val) {
int exp2 = Math.getExponent(val);
int exp10 = (int) Math.floor(Math.log10(Math.abs(val)));
// check for any mismatch between the exact decimal and
// the round-trip representation.
int rightmost_bits = (52 - exp2) - (16 - exp10);
// create bitmask for rightmost bits
long mask = (1L << rightmost_bits) - 1;
// test if all rightmost bits are 0's (i.e., no rounding)
return (Double.doubleToLongBits(val) & mask) == 0;
}
Edit: the above function could be even shorter
public static boolean isExactFloat(double val) {
int exp2 = Math.getExponent(val);
int exp10 = (int) Math.floor(Math.log10(Math.abs(val)));
long bits = Double.doubleToLongBits(val);
// test if at least n rightmost bits are 0's (i.e., no rounding)
return Long.numberOfTrailingZeros(bits) >= 36 - exp2 + exp10;
}
Demo

Create a BigDecimal from it and catch java.lang.ArithmeticException which it will throw if there is a non-terminating decimal expansion.

Java double can only represent terminating binary fractions. Doing the conversion to double may hide issues, so I think it is better to work from the String representation. The conversion to BigDecimal is exact if the String represents a number. So is conversion from float or double to BigDecimal. Here are test functions for exact representation as float or double:
public static boolean isExactDouble(String data) {
BigDecimal rawBD = new BigDecimal(data);
double d = rawBD.doubleValue();
BigDecimal cookedBD = new BigDecimal(d);
return cookedBD.compareTo(rawBD) == 0;
}
public static boolean isExactFloat(String data) {
BigDecimal rawBD = new BigDecimal(data);
float d = rawBD.floatValue();
BigDecimal cookedBD = new BigDecimal(d);
return cookedBD.compareTo(rawBD) == 0;
}

It is not clear whether your issue has to do with precision (representing 0.24 accurately) or recurring numbers, like 1 / 3.0.
In general precision issues will always creep in if you use the conventional floating point representations.
If precision is a real problem for you, you should look at using BigDecimal. While not as flexible as double it has other advantages like arbitrary precision, and you can also control the rounding behaviour in non-exact calculations (like recurring decimal values).
If all you are after is precision control, you might want to look at the Apache Commons Math Precision class.

You could just compare the double and the float?
public static boolean isExactFloat(double d, float f) {
return d == f;
}
Demo

Related

BigDecimal.setScale(..) changes precisions to 0

I'm using java Corretto 11 and trying to serialize BigDecimal value into avro format.
The value:
BigDecimal v = BigDecimal.valueOf(16858.7109375)) //scale = 7, precision = 12
then round it
v.setScale(5, RoundingMode.HALF_UP) //scale = 5, precision = 0
It happens because BigDecimal's internals call static factory method
public static BigDecimal valueOf(long unscaledVal, int scale) {
if (scale == 0)
return valueOf(unscaledVal);
else if (unscaledVal == 0) {
return zeroValueOf(scale);
}
return new BigDecimal(unscaledVal == INFLATED ?
INFLATED_BIGINT : null,
unscaledVal, scale, 0); //HERE it sets precision = 0
}
Not a big deal.. however avro's validator and converter has different opinion about it :)
//org.apache.avro.Conversions (lib avro-1.10.0)
private static BigDecimal validate(final LogicalTypes.Decimal decimal, BigDecimal value) { //scale = 5, precision = 0
... //nothing happens here
int precision = decimal.getPrecision(); // 9 as defined in avro schema
int valuePrecision = value.precision(); // this changes internal precision to 10 (total digits count after scaling)
if (valuePrecision > precision) { //this is true, and so serialization fails
if (scaleAdjusted) {
throw new AvroTypeException("Cannot encode decimal with precision " + valuePrecision + " as max precision "
+ precision + ". This is after safely adjusting scale from " + valueScale + " to required " + scale);
} else {
throw new AvroTypeException(
"Cannot encode decimal with precision " + valuePrecision + " as max precision " + precision);
}
}
I'm not sure how to fix it.. Is this a bug of BigDecimals internal functions or avro converter? Can I hack it somehow?
BigDecimal a = BigDecimal.valueOf(16858.7109375); final
BigDecimal b = a.setScale(5, RoundingMode.HALF_UP);
System.out.println("Scale: " + b.scale() + ", precision: " + b.precision());
//Scale: 5, precision: 10
It is true, that internally BigDecimal b is showing precision zero, but for each public contract, you will see 10.
In your Avro, if you have "precision": 9 (max precision) Avro validation will throw an exception because it cannot do downsizing in any way.
You can change your Avro to add bigger max precision (but you have to start from consumers first), or you can just ensure that bigger number will not occur there

Convert double into BigRational (two BigInteger for numerator/denominator)

I have a custom made BigRational class in java.
It is implemented as two BigInteger, representing numerator and denominator.
I have a "from string" method that take input in the form "-1234/43"
but I would like to implement a from double/from float;
I'm not scare of generating a very large number, but I would like to keep all the precision present in the floating point representation; thus if I converted them in some decimal representation I would lose precision thanks to rounding.
-How do I generate a pair of BigIntegers that interpreted as numerator/denominator represents the same exact number as a given float/double?
(Hopefully by being in Java I do not need to worry about bigendian/littleendian, but I would like a confermation too)
So, thanks to a good friend I have found a good solution, so I will post it here for anyone in need.
It is not using any string representation so it should also be quite on the fast side.
I have tested it "reasonably" and It seams to work and to keep the exact representation.
Of course, we should still add some 'if' to handle NANs.
final static int mantissaBits=53;
public static BigRational from(double num){
int exponent=Math.getExponent(num);
long man=Math.round(Math.scalb(num, mantissaBits-exponent));
long den=Math.round(Math.scalb(1.0, mantissaBits-exponent));
return new BigRational(BigInteger.valueOf(man),BigInteger.valueOf(den));
}
Caveat: Not all numbers are rational, e.g. PI is not a rational number. However, given that double (and float) have limited precision, there are a limited number of digits in a floating-point value, so you can always find a rational number for that. E.g. Math.PI is a double with the value 3.141592653589793. That number is the rational number 3_141_592_653_589_793 / 1_000_000_000_000_000.
Understanding the caveat that floating-point values aren't accurate, you can find the rational number with the help of BigDecimal, then normalize the rational number using BigInteger.gcd().
Like this:
static void printAsRational(double value) {
printAsRational(BigDecimal.valueOf(value));
}
static void printAsRational(float value) {
printAsRational(new BigDecimal(Float.toString(value)));
}
static void printAsRational(BigDecimal value) {
BigInteger numerator, denominator;
if (value.signum() == 0) {
// Zero is 0 / 1
numerator = BigInteger.ZERO;
denominator = BigInteger.ONE;
} else {
BigDecimal bd = value.stripTrailingZeros(); // E.g. 1.20 -> 1.2
if (bd.scale() < 0)
bd = bd.setScale(0); // E.g. 1.7e3 -> 1700
numerator = bd.unscaledValue(); // E.g. 1.25 -> 125
denominator = BigDecimal.valueOf(1, -bd.scale()).toBigInteger(); // E.g. 1.25 -> 100
// Normalize, e.g. 12/8 -> 3/2
BigInteger gcd = numerator.gcd(denominator);
if (! gcd.equals(BigInteger.ONE)) {
numerator = numerator.divide(gcd);
denominator = denominator.divide(gcd);
}
}
System.out.println(value + " = " + numerator + " / " + denominator);
}
Tests
printAsRational(Math.PI);
printAsRational(Math.E);
printAsRational(1.25);
printAsRational(1);
printAsRational(0);
printAsRational(-1.25);
printAsRational(1.25e9);
printAsRational(1.25e-9);
Output
3.141592653589793 = 3141592653589793 / 1000000000000000
2.718281828459045 = 543656365691809 / 200000000000000
1.25 = 5 / 4
1.0 = 1 / 1
0.0 = 0 / 1
-1.25 = -5 / 4
1.25E+9 = 1250000000 / 1
1.25E-9 = 1 / 800000000

Get a numer decimal part as Integer using only math

Edit: This has to do with how computers handle floating point operations, a fact that every programmer faces once in a lifetime. I didn't understand this correctly when I asked the question.
I know the simplest way to start dealing with this would be:
val floatNumber: Float = 123.456f
val decimalPart = floatNumber - floatNumber.toInt() //This would be 0.456 (I don't care about precision as this is not the main objective of my question)
Now in a real world with a pen and a piece of paper, if I want to "convert" the decimal part 0.456 to integer, I just need to multiply 0.456 * 1000, and I get the desired result, which is 456 (an integer number).
Many proposed solutions suggest splitting the number as string and extracting the decimal part this way, but I need the solution to be obtained mathematically, not using strings.
Given a number, with an unknown number of decimals (convert to string and counting chars after . or , is not acceptable), I need to "extract" it's decimal part as an integer using only math.
Read questions like this with no luck:
How to get the decimal part of a float?
How to extract fractional digits of double/BigDecimal
If someone knows a kotlin language solution, it would be great. I will post this question also on the math platform just in case.
How do I get whole and fractional parts from double in JSP/Java?
Update:
Is there a "mathematical" way to "calculate" how many decimals a number has? (It is obvious when you convert to string and count the chars, but I need to avoid using strings) It would be great cause calculating: decimal (0.456) * 10 * number of decimals(3) will produce the desired result.
Update 2
This is not my use-case, but I guess it will clarify the idea:
Suppose you want to calculate a constant(such as PI), and want to return an integer with at most 50 digits of the decimal part of the constant. The constant doesn't have to be necessarily infinite (can be for example 0.5, in which case "5" will be returned)
I would just multiply the fractional number by 10 (or move the decimal point to the right) until it has no fractional part left:
public static long fractionalDigitsLong(BigDecimal value) {
BigDecimal fractional = value.remainder(BigDecimal.ONE);
long digits;
do {
fractional = fractional.movePointRight(1); // or multiply(BigDecimal.TEN)
digits = fractional.longValue();
} while (fractional.compareTo(BigDecimal.valueOf(digits)) != 0);
return digits;
}
Note 1: using BigDecimal to avoid floating point precision problems
Note 2: using compareTo since equals also compares the scale ("0.0" not equals "0.00")
(sure the BigDecimal already knows the size of the fractional part, just the value returned by scale())
Complement:
If using BigDecimal the whole problem can be compressed to:
public static BigInteger fractionalDigits(BigDecimal value) {
return value.remainder(BigDecimal.ONE).stripTrailingZeros().unscaledValue();
}
stripping zeros can be suppressed if desired
I am not sure if it counts against you on this specific problem if you use some String converters with a method(). That is one way to get the proper answer. I know that you stated you couldn't use String, but would you be able to use Strings within a Custom made method? That could get you the answer that you need with precision. Here is the class that could help us convert the number:
class NumConvert{
String theNum;
public NumConvert(String theNum) {
this.theNum = theNum;
}
public int convert() {
String a = String.valueOf(theNum);
String[] b = a.split("\\.");
String b2 = b[1];
int zeros = b2.length();
String num = "1";
for(int x = 0; x < zeros; x++) {
num += "0";
}
float c = Float.parseFloat(theNum);
int multiply = Integer.parseInt(num);
float answer = c - (int)c;
int integerForm = (int)(answer * multiply);
return integerForm;
}
}
Then within your main class:
public class ChapterOneBasics {
public static void main(String[] args) throws java.io.IOException{
NumConvert n = new NumConvert("123.456");
NumConvert q = new NumConvert("123.45600128");
System.out.println(q.convert());
System.out.println(n.convert());
}
}
output:
45600128
456
Float or Double are imprecise, just an approximation - without precision. Hence 12.345 is somewhere between 12.3449... and 12.3450... .
This means that 12.340 cannot be distinghuished from 12.34. The "decimal part" would be 34 divided by 100.
Also 12.01 would have a "decimal part" 1 divided by 100, and too 12.1 would have 1 divided by 10.
So a complete algorith would be (using java):
int[] decimalsAndDivider(double x) {
int decimalPart = 0;
int divider = 1;
final double EPS = 0.001;
for (;;) {
double error = x - (int)x;
if (-EPS < error && error < EPS) {
break;
}
x *= 10;
decimalPart = 10 * decimalPart + ((int)(x + EPS) % 10);
divider *= 10;
}
return new int[] { decimalPart, divider };
}
I posted the below solution yesterday after testing it for a while, and later found that it does not always work due to problems regarding precision of floats, doubles and bigdecimals. My conclusion is that this problem is unsolvable if you want infinite precision:
So I re-post the code just for reference:
fun getDecimalCounter(d: Double): Int {
var temp = d
var tempInt = Math.floor(d)
var counter = 0
while ((temp - tempInt) > 0.0 ) {
temp *= 10
tempInt = Math.floor(temp)
counter++
}
return counter
}
fun main(args: Array <String> ) {
var d = 3.14159
if (d < 0) d = -d
val decimalCounter = getDecimalCounter(d)
val decimalPart = (d - Math.floor(d))
var decimalPartInt = Math.round(decimalPart * 10.0.pow(decimalCounter))
while (decimalPartInt % 10 == 0L) {
decimalPartInt /= 10
}
println(decimalPartInt)
}
I dropped floats because of lesser precision and used doubles.
The final rounding is also necessary due to precision.

Check if a double = 1/3

I have made a function that converts a double to a simplified fraction in Java:
public static int gcm(int a, int b) {
return b == 0 ? a : gcm(b, a % b);
}
public static String toFraction(double d) {
int decimals = String.valueOf(d).split("\\.")[1].length();
int mult = (int) Math.pow(10, decimals);
int numerator = (int) (d * mult);
int denominator = mult;
// now simplify
int gcm = gcm(numerator, denominator);
numerator /= gcm;
denominator /= gcm;
return numerator + "/" + denominator;
}
It works, except for the fact that if I use toFraction(1.0/3), this will, understandably, return "715827882/2147483647". How may I fix this to return "1/3"?
You have to allow for a certain error and not all fractions can be exactly represented as scalar values.
public static String toFraction(double d, double err) {
String s = Long.toString((long) d);
d -= (long) d;
if (d > err) {
for (int den = 2, max = (int) (1 / err); den < max; den++) {
long num = Math.round(d * den);
double d2 = (double) num / den;
if (Math.abs(d - d2) <= err)
return (s.equals("0") ? "" : s + " ") + num +"/"+den;
}
}
return s;
}
public static void main(String... args) {
System.out.println(toFraction(1.0/3, 1e-6));
System.out.println(toFraction(1.23456789, 1e-6));
System.out.println(toFraction(Math.E, 1e-6));
System.out.println(toFraction(Math.PI, 1e-6));
for (double d = 10; d < 1e15; d *= 10)
System.out.println(toFraction(Math.PI, 1.0 / d));
}
prints
1/3
1 19/81
2 719/1001
3 16/113
3 1/5
3 1/7
3 9/64
3 15/106
3 16/113
3 16/113
3 3423/24175
3 4543/32085
3 4687/33102
3 14093/99532
3 37576/265381
3 192583/1360120
3 244252/1725033
3 2635103/18610450
Note: this finds the 21/7, 333/106 and 355/113 approximations for PI.
No double value is equal to one third, so the only way your program can be made to print 1/3 is if you change the specification of the method to favour "nice" answers rather than the answer that is technically correct.
One thing you could do is choose a maximum denominator for the answers, say 100, and return the closest fraction with denominator 100 or less.
Here is how you could implement this using Java 8 streams:
public static String toFraction(double val) {
int b = IntStream.rangeClosed(1, 100)
.boxed()
.min(Comparator.comparingDouble(n -> Math.abs(val * n - Math.round(val * n))))
.get();
int a = (int) Math.round(val * b);
int h = gcm(a, b);
return a/h + "/" + b/h;
}
There is no nice approach to this. double is not very good for this sort of thing. Note that BigDecimal can't represent 1/3 either, so you'll have the same problem with that class.
There are nice ways to handle this but you will need to look at special cases. For example, if the numerator is 1 then the fraction is already reduced and you simply strip out the decimal places and return what you were given.

Determine Number of Decimal Place using BigDecimal

I was interested to have the following getNumberOfDecimalPlace function:
System.out.println("0 = " + Utils.getNumberOfDecimalPlace(0)); // 0
System.out.println("1.0 = " + Utils.getNumberOfDecimalPlace(1.0)); // 0
System.out.println("1.01 = " + Utils.getNumberOfDecimalPlace(1.01)); // 2
System.out.println("1.012 = " + Utils.getNumberOfDecimalPlace(1.012)); // 3
System.out.println("0.01 = " + Utils.getNumberOfDecimalPlace(0.01)); // 2
System.out.println("0.012 = " + Utils.getNumberOfDecimalPlace(0.012)); // 3
May I know how can I implement getNumberOfDecimalPlace, by using BigDecimal?
The following code doesn't work as expected:
public static int getNumberOfDecimalPlace(double value) {
final BigDecimal bigDecimal = new BigDecimal("" + value);
final String s = bigDecimal.toPlainString();
System.out.println(s);
final int index = s.indexOf('.');
if (index < 0) {
return 0;
}
return s.length() - 1 - index;
}
The following get printed :
0.0
0 = 1
1.0
1.0 = 1
1.01
1.01 = 2
1.012
1.012 = 3
0.01
0.01 = 2
0.012
0.012 = 3
However, for case 0, 1.0, it doesn't work well. I expect, "0" as result. But they turned out to be "0.0" and "1.0". This will return "1" as result.
Combining Turismo, Robert and user1777653's answers, we've got:
int getNumberOfDecimalPlaces(BigDecimal bigDecimal) {
return Math.max(0, bigDecimal.stripTrailingZeros().scale());
}
stripTrailingZeros() ensures that trailing zeros are not counted (e.g. 1.0 has 0 decimal places).
scale() is more efficient than String.indexOf().
A negative scale() represents zero decimal places.
There you have it, the best of both worlds.
This code:
int getNumberOfDecimalPlaces(BigDecimal bigDecimal) {
String string = bigDecimal.stripTrailingZeros().toPlainString();
int index = string.indexOf(".");
return index < 0 ? 0 : string.length() - index - 1;
}
... passes these tests:
assertThat(getNumberOfDecimalPlaces(new BigDecimal("0.001")), equalTo(3));
assertThat(getNumberOfDecimalPlaces(new BigDecimal("0.01")), equalTo(2));
assertThat(getNumberOfDecimalPlaces(new BigDecimal("0.1")), equalTo(1));
assertThat(getNumberOfDecimalPlaces(new BigDecimal("1.000")), equalTo(0));
assertThat(getNumberOfDecimalPlaces(new BigDecimal("1.00")), equalTo(0));
assertThat(getNumberOfDecimalPlaces(new BigDecimal("1.0")), equalTo(0));
assertThat(getNumberOfDecimalPlaces(new BigDecimal("1")), equalTo(0));
assertThat(getNumberOfDecimalPlaces(new BigDecimal("10")), equalTo(0));
assertThat(getNumberOfDecimalPlaces(new BigDecimal("10.1")), equalTo(1));
assertThat(getNumberOfDecimalPlaces(new BigDecimal("10.01")), equalTo(2));
assertThat(getNumberOfDecimalPlaces(new BigDecimal("10.001")), equalTo(3));
... if that is indeed what you want. The other replies are correct, you have to use BigDecimal all the way through for this rather than double/float.
Without having to convert to String, it should be more efficient to use the scale directly:
private int getNumberOfDecimalPlaces(BigDecimal bigDecimal)
{
int scale = bigDecimal.stripTrailingZeros().scale();
return scale>0?scale:0;
}
That should do it
int getNumberOfDecimalPlace(BigDecimal number) {
int scale = number.stripTrailingZeros().scale();
return scale > 0 ? scale : 0;
}
If you really get doubles i recommend formating them first as strings before creating the BigDecimal. At least that has worked for me: How to check if a double has at most n decimal places?
Depending on how many digits you expect you can either use standard formating like
String.valueOf(doubleValue);
or you could use specialised formatting to avoid exponential format
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);
When you have a BigDecimal you can simply call scale() to get the number of decimal places.
It's not your code that's wrong, but your expectations. double is based on a binary floating point representation and completely unfit for accurately representing decimal fractions. Decimal 0.1 e.g. has an infinite number of digits when represented in binary, thus it gets truncated and when converted back to decimal, you get erros in the least significant digits.
If you use BigDecimal exclusively, your code will work as expected.
Try this:
Math.floor(Math.log(x) / Math.log(10))
0.001 = -3
0.01 = -2
0.1 = -1
1 = 0
10 = 1
100 = 2
How about having a look at the javadoc of BigDecimal. I'm not sure, but I'd give getScale and getPercision a try.
The best way to get a BigDecimal with a specified number of decimal places is by using the setscale method over it. Personally I like to also use the rounding version of the method (see the link below):
http://java.sun.com/j2se/1.5.0/docs/api/java/math/BigDecimal.html#setScale(int,%20int)
If you're wanting to get the number of decimal positions that a BigDecimal is currently set at call the associated scale() method.
Best option I have found so far (not needing toString + index):
public static int digitsStripTrailingZero(BigDecimal value)
{
return digits(value.stripTrailingZeros());
}
public static int digits(BigDecimal value)
{
return Math.max(0, value.scale());
}
Michael Borgwardt answer is the correct one. As soon as you use any double or float, your values are already corrupted.
To provide a code example:
System.out.println("0 = " + BigDecimalUtil.getNumberOfDecimalPlace("0")); // 0
System.out.println("1.0 = " + BigDecimalUtil.getNumberOfDecimalPlace("1.0")); // 0
System.out.println("1.01 = " + BigDecimalUtil.getNumberOfDecimalPlace(new BigDecimal("1.01"))); // 2
System.out.println("1.012 = " + BigDecimalUtil.getNumberOfDecimalPlace(new BigDecimal("1.012"))); // 3
System.out.println("0.01 = " + BigDecimalUtil.getNumberOfDecimalPlace("0.01")); // 2
System.out.println("0.012 = " + BigDecimalUtil.getNumberOfDecimalPlace("0.012")); // 3
System.out.println("0.00000000000000000012 = " + BigDecimalUtil.getNumberOfDecimalPlace("0.00000000000000000012")); // 20
And an overloaded version of getNumberOfDecimalPlace so you could use it with BigDecimal or String:
public static int getNumberOfDecimalPlace(String value) {
final int index = value.indexOf('.');
if (index < 0) {
return 0;
}
return value.length() - 1 - index;
}
public static int getNumberOfDecimalPlace(BigDecimal value) {
return getNumberOfDecimalPlace(value.toPlainString());
}
Why not just change your code to get a doubles decimal places?
public static int getNumberOfDecimalPlace(double value) {
//For whole numbers like 0
if (Math.round(value) == value) return 0;
final String s = Double.toString(value);
System.out.println(s);
final int index = s.indexOf('.');
if (index < 0) {
return 0;
}
return s.length() - 1 - index;
}

Categories