3

I have been trying to find a way to make sure the length of the double "value" is not larger than 10. However, this is becoming difficult to program since I am not rounding it to a certain number of decimal places.

For example, 1234567.8912 and 12.345678912 are both larger than 10 digits however, they will have to be rounded to a different number of decimal places. My logic was to find where the decimal point occurs and rounding the double to "10 - number of digits before the decimal place".

I created two different methods and both methods won't seem to work correctly.

    if ((Double.toString(value)).length()> 10){
        int counter = 0;
        double no_of_dec = 1;
        double no_of_int = 1;
        double new_value = 0;
        for (int i = 0; i< (Double.toString(value)).length(); i++){
            if ( (Character.toString((Double.toString(value)).charAt(i))).equals(".") ){
                counter = 1;
            } if (counter == 1){
                no_of_dec = no_of_dec * 10;
            } else if (counter == 0){
                no_of_int = no_of_int * 10;
            }
        } 
        no_of_dec = no_of_dec / (no_of_int);
        new_value = (double)Math.round(value * 100d/100d);
        return Double.toString(new_value);
    } else {
        return Double.toString(value);
    }


    if ((Double.toString(value)).length()> 10){
        double no_of_dec = 0;
        double no_of_int = 0;
        double new_value = 0;
        for (int i = 0; i< (Double.toString(value)).length(); i++){
            while (!(Character.toString((Double.toString(value)).charAt(i))).equals(".")){
                no_of_int = no_of_int + 1;
            }
        }
        no_of_dec = (Double.toString(value)).length() - no_of_int;
        no_of_dec = no_of_dec * 10;
        new_value = (double)Math.round(value * no_of_dec/no_of_dec);
        return Double.toString(new_value);
    } else {
        return Double.toString(value);
    }
}
akash
  • 22,664
  • 11
  • 59
  • 87
assassinweed2
  • 71
  • 1
  • 3
  • 9
  • If the number is `0.1234567891` does the leading zero get counted as a digit? – 4castle Dec 04 '16 at 23:30
  • what does _"they will have to be rounded to a different number of decimal places"_ mean? However, could you give an example as an input and expected output. – Soner from The Ottoman Empire Dec 04 '16 at 23:34
  • @4castle yes it does – assassinweed2 Dec 04 '16 at 23:36
  • @snr so for example, 1.3456789556 and 13456.789556 are both larger than 10 digits. However, they both have a different number of decimal places. Therefore, for the first example you'll have to round it to 9 decimal places and the second example you'll have to round it to 5 decimal places in order to make them the length of 10 digits. – assassinweed2 Dec 04 '16 at 23:37
  • 1
    Doubles don't have decimal digits. They have binary digits, and the two are incommensurable. What you're asking for is impossible in principle. If you want decimal digits, use a decimal radix, e.g. `BigDecimal`. – user207421 Dec 04 '16 at 23:41
  • why 9 and why 5 could you explain more? @assassinweed2 – Soner from The Ottoman Empire Dec 04 '16 at 23:42

2 Answers2

3

I did it this way:

private static BigDecimal getRounded(double n, int totalDigits) {

    String nString = Double.toString(n); // transform to string to make the job easier

    if(nString.contains(".")) {
        int dotPos = nString.indexOf("."); // = number of digits before the decimal point

        int remainingDigits = totalDigits - dotPos; // = remaining digits after the decimal point

        return new BigDecimal(nString).setScale(remainingDigits, BigDecimal.ROUND_HALF_UP); // round
    }

    return new BigDecimal(n);
}

This was my test:

double n = 1234567.8912;
System.out.println(getRounded(n, 10));

n = 12.345678915;
System.out.println(getRounded(n, 10));

And this the result:

1234567.891
12.34567892

Demo: http://ideone.com/my7eB2

BackSlash
  • 21,927
  • 22
  • 96
  • 136
0

BigDecimal has an operation for doing this:

double truncate(double value) {
    BigDecimal b = new BigDecimal(String.valueOf(value));
    b = b.round(new MathContext(10));
    return b.doubleValue();
}

There is a reason for using the BigDecimal(String) constructor and not the BigDecimal(double) constructor. doubles and floats are not base 10 numbers, so they don’t exactly represent the values we see when we print them; the constructor documentation has a good illustration of this. This is not unique to Java and is true in most languages.

Update:

4castle points out that the above won’t work for nonzero numbers with a magnitude less than 1, since BigDecimal doesn’t consider the zero integer part a significant digit. You can work around that:

double truncate(double value) {
    int precision = 10;
    if (value != 0 && Math.abs(value) < 1) {
        precision--;
    }

    BigDecimal b = new BigDecimal(String.valueOf(value));
    b = b.round(new MathContext(precision));
    return b.doubleValue();
}

Update 2:

BackSlash points out that it’s more complex than the above for nonzero numbers between -1 and 1. I’m sure it’s possible to calculate the exact precision needed, but I think it’s simpler to just eliminate the issue by adding 1 for rounding purposes:

double truncate(double value) {
    BigDecimal b = new BigDecimal(String.valueOf(value));

    MathContext context = new MathContext(10);
    if (value > 0 && value < 1) {
        b = b.add(BigDecimal.ONE);
        b = b.round(context);
        b = b.subtract(BigDecimal.ONE);
    } else if (value < 0 && value > -1) {
        b = b.subtract(BigDecimal.ONE);
        b = b.round(context);
        b = b.add(BigDecimal.ONE);
    } else {
        b = b.round(context);
    }

    return b.doubleValue();
}
VGR
  • 40,506
  • 4
  • 48
  • 63
  • 1
    This is definitely the best solution, but I've found that the leading zero (see my comment above) isn't counted as part of the precision, so it gives 1 too many digits in that case. – 4castle Dec 05 '16 at 00:03
  • 2
    Turning it back into a `double` destroys everything that has been done by `BigDecimal`. And `BigDecimal` does have a constructor from `double`. – user207421 Dec 05 '16 at 02:28
  • 1
    In fact it doesn't just return one more digit with numbers less than 1. It returns one more digit **per leading zero**. For example, I tried this method with `0.00034567089445515`: It returns `0.0003456708945` which is 14 digits long: 10 + 4 leading zeros. If I enter try with `0.0000000034567089445515` it returns `0.000000003456708945`, Which is 19 digits long: 10 + 9 leading zeros. [Demo here](http://ideone.com/BqJVZg) – BackSlash Dec 05 '16 at 14:41
  • @EJP I explained why the BigDecimal(double) constructor should be avoided; the javadoc explains it well. Can you give an example of an input value that is destroyed by the conversion? Every input value I tried worked. – VGR Dec 05 '16 at 15:04
  • @BackSlash Right you are. Updated. – VGR Dec 05 '16 at 15:29
  • Converting the `double` to a string and then using `new BigDecimal(String)` doesn't magically sidestep what `new BigDecimal(double)` does; and turning the result back int a double just gives you the whole problem back again. If you want decimal digits you have to use a decimal radix. No two ways about it. – user207421 Mar 23 '19 at 22:52