Reputation: 2002
How to format the decimal number precision like given below:
double d = 1/3
returns 0.3333333333333333
and mathematically 3 is repeated infinitely.
d = ((double)3)/ (double)41;
returns 0.07317073170731707
and here 07317 is repeated.
Now, the ask to format the output like below
0.3333333333333333
should be formatted to 0.(3)
as 3 is repeated.
Similarly 0.07317073170731707
should be formatted to 0.(07317)
as 07317 repeated
I had looked into DecimalFormat
class but I am able to format only number of precisions.
public static void main(String[] args) {
DecimalFormat formatter = new DecimalFormat("#0.'('##')'");
double d = ((double)1)/ (double)3;
System.out.println("formatted "+formatter.format(d));
System.out.println(d);
d = ((double)1)/ (double)2;
System.out.println("formatted "+formatter.format(d));
System.out.println(d);
d = ((double)3)/ (double)41;
System.out.println("formatted "+formatter.format(d));
System.out.println(d);
}
Output:
formatted 0.33()
0.3333333333333333
formatted 0.5()
0.5
formatted 0.07()
0.07317073170731707
Is there any built-in class in Java available to achieve the same?
Upvotes: 3
Views: 298
Reputation: 154
As pointed out in the comments, if you only have a String representation, to begin with, there is no way to tell if there is actually some repeating part to it or if the precision simply is not high enough.
However, from your sample, it seems that you actually want to find the repeating part of fractions where you know both the numerator and denominator as an exact integer. That is not too hard of a problem; for example, you can look at https://codereview.stackexchange.com/questions/87522/decimal-expansion-of-a-rational-number
=== Update ===
I was working on a class representing a rational number with numerator and denominator as BigIntegers (and assumed to be reduced as far as possible). There I implemented a method to produce a representation as decimal String and came up with the following:
public String toDecimalString() {
var result = new StringBuilder();
var remainders = new HashMap<BigInteger, Integer>();
if(!isNonNegative()) result.append("-");
var divAndRem = numerator.abs().divideAndRemainder(denominator);
result.append(divAndRem[0].toString());
if(isWholeNumber()) return result.toString();
result.append(".");
int currentPosition = result.length();
remainders.put(divAndRem[1], currentPosition);
while(!divAndRem[1].equals(BigInteger.ZERO)) {
divAndRem = divAndRem[1].multiply(BigInteger.TEN).divideAndRemainder(denominator);
result.append(divAndRem[0].toString());
if(remainders.containsKey(divAndRem[1])) {
int periodStart = remainders.get(divAndRem[1]);
return result.substring(0, periodStart)+"["+result.substring(periodStart)+"]";
}
remainders.put(divAndRem[1], ++currentPosition);
}
return result.toString();
}
Upvotes: 2
Reputation: 109613
Unfortunately I do not know of such a class, maybe mathematicians know. The problem is that one a part of the infinite representations are turned to finite ones, and calculating with repeated groups must be done symbolically, not with some immediate binary numbers. (A nice student project maybe.)
double
is flawed. One could make one's own class based on BigDecimal
that can identify repeating decimals, and store such "infinite" numbers. Many infinite numbers like π still will be unusable.
Or you can create a rational number class with (numerator, denominator).
And for some rare cases you can avoid repeating decimals by switching the number base, as Integer/Long can work with diferent bases and one can build upon that:
0.1 base 3 = 0.33333... base 10
Upvotes: 1
Reputation: 31
You can make a method that takes into account simple repetitions, like the one below.
public static double reformatNumber(double number) {
String s = String.valueOf(number);
String[] split = s.split("\\.");
String result = split[1].substring(0,8); // Length to consider
String actualDigit = "";
String lastDigit = "";
int counter = 0;
for (int i = 1; i <= result.length(); i++) {
actualDigit = result.substring(i-1,i);
if (actualDigit.equals(lastDigit)) {
counter++;
} else {
counter = 0;
}
lastDigit = actualDigit;
if (counter == 3) { // Number of repetitions in a row required
result = result.substring(0,i-1);
}
}
s = split[0]+"."+result;
return Double.parseDouble(s);
}
If you want consider repetitions of two or more simply modify the last method to save two digits at once, like this.
public static double reformatNumber(double number) {
String s = String.valueOf(number);
String[] split = s.split("\\.");
String result = split[1].substring(0,8);
String actualDigit = "";
String lastDigit = "";
int counter = 0;
for (int i = 1; i <= result.length(); i++) {
actualDigit = result.substring(i-1,i);
if (actualDigit.equals(lastDigit)) {
counter++;
} else if (i%4 == 0 && (actualDigit+lastDigit).equals(result.substring(i-4,i-2))) {
// Every four cycles checks if the last digit and the current one
// are equals to the last two digits before the last digit
counter++;
} else {
counter = 0;
}
lastDigit = actualDigit;
if (counter == 3) {
result = result.substring(0,i-1);
break;
}
}
s = split[0]+"."+result;
return Double.parseDouble(s);
}
Upvotes: 0