Shiva
Shiva

Reputation: 2002

How to format decimal number precision

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

Answers (3)

floxbr
floxbr

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

Joop Eggen
Joop Eggen

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

MIR
MIR

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

Related Questions