1

Possible Duplicate:
How to round a number to n decimal places in Java

I want to set a specified number of decimal digits in a float (or double), with a method in this form

public float decimalDigits(int x, float n){
....
}

for example

->if I have

float n1=36.58529

the line

float n2=decimalDigits(2, n1);

should return

n2=36.59

->if n1 is:

float n1=36.58329

the line

float n2=decimalDigits(2, n1);

should return

n2=36.58

the line

float n2=decimalDigits(1, n2);

should return

n2=36.6

etc

Community
  • 1
  • 1
AndreaF
  • 11,106
  • 24
  • 92
  • 156
  • 4
    Are you aware that `float` and `double` don't store their values in *decimal* but in *binary*? Arbitrary decimal numbers can't be stored exactly in a float or double, so you will get representation errors (meaning that the actual value will be slightly bigger or smaller than the value you intended to store). Knowing this, are you sure that this is what you want to do? – Mark Byers Dec 24 '12 at 00:43
  • You can find more info about formatting floats here: http://stackoverflow.com/questions/703396/how-to-nicely-format-floating-types-to-string – tcb Dec 24 '12 at 03:20
  • 1
    The solution of Silverstorm works. Thanks everybody. – AndreaF Dec 24 '12 at 03:36
  • No it doesn't. See [here](http://stackoverflow.com/questions/703396/how-to-nicely-format-floating-types-to-string) for proof. – user207421 Dec 24 '12 at 04:56
  • 3
    @EJP You have linked the proof that your solution doesn't works, not the proof that there aren't possible solution to meet the question problem – Silverstorm Dec 24 '12 at 05:22

3 Answers3

3

Use this

public float decimalDigits(int decimaldigits, float x){
            final NumberFormat numFormat = NumberFormat.getNumberInstance();
            numFormat.setMaximumFractionDigits(decimaldigits);
            final String resultS = numFormat.format(x);
            String parsable=resultS.replace(".", "");
            parsable=resultS.replace(",", ".");
            float ris=Float.parseFloat(parsable);
            return ris;
        }

I have added the String replacement to the code to avoid Parsing issue caused by the dot convention (for example 1234.34 becomes 1.234,34 after the formatting causing error in reparsing in float)

If the your is simply a format visualization problem, you could also use the String and doesn't matter that floating point variables don't have decimal places, so this is another valid method:

public String decimalDigits(int decimaldigits, float x){
        final NumberFormat numFormat = NumberFormat.getNumberInstance();
        numFormat.setMaximumFractionDigits(decimaldigits);
        final String resultS = numFormat.format(x);
        return resultS;
    }

If someone has doubts about validity of this solution, should ask for details or try to compile the code and test it before downvote, thanks. The code is tested and works like a charm.

WARNING

Clearly you have to pass float because the method use Float.parseFloat, if you want to pass a double you have to use a cast to float before pass it in the method, otherwise you have to change all the method primitive and parse from float to double. Double and float are different.

Silverstorm
  • 12,730
  • 2
  • 34
  • 52
  • The first version doesn't work, and the second version doesn't even compile. A method declared as returning a float cannot return a String. -1 – user207421 Dec 24 '12 at 02:37
  • 2
    @EJP Question edited. In the second version, clearly I have forgot to edit the first version from float to String, now the code compile and works. In the first version the issue was caused by a little problem with the dot convention, with a String replace works fine. I have tested it and works fine. – Silverstorm Dec 24 '12 at 03:13
  • I would recommend you to read http://stackoverflow.com/questions/703396/how-to-nicely-format-floating-types-to-string – tcb Dec 24 '12 at 03:17
  • 2
    @tcb if you try the code this works – Silverstorm Dec 24 '12 at 03:18
  • Sorry, I commented in wrong place :) – tcb Dec 24 '12 at 03:19
  • Tested how? I suggest you read stackoverflow.com/a/12684082/207421 before you commit yourself any further. – user207421 Dec 24 '12 at 04:44
  • As I have said, if according to you, this doesn't work, give me a float that lead the method to fail the conversion. I have tried to cycle the method with a random generated float fixed to 1,2 and 3 decimal digits and haven't get any wrong converted value. – Silverstorm Dec 24 '12 at 05:03
  • I've done that, see the linked post. Ignoring the counter evidence is unscientific. – user207421 Dec 24 '12 at 05:11
  • 1
    @EJP Your post proof that the scientific evidence is that you wasn't able to solve the problem operating directly with the float, not that the also my solution is wrong. – Silverstorm Dec 24 '12 at 05:18
  • @Silverstorm My post is a completely redundant *illustration* that floating point cannot represent a precise number of decimal places unless the fraction is a negative power of two: for example, 0.5, 0.25, 0.125, ... so, no method that returns a float or double can solve this problem. If you have a counterproof of this mathematical fact you haven't provided it. If there is something wrong with my code you haven't said what it is. You've done nothing here but assert, *without* proof, that your code works, presumably for all possible inputs, otherwise it would be pointless. It can't. – user207421 Dec 24 '12 at 07:10
  • @EJP Float are saved as binary but this not mean that there aren't way to map their string representation and operate on it, the question ask for a working method and this works with a random float generator and 15 minutes of execution in a while(true) cycle on a core i7 any bad result was returned. The proof? Everybody can run the code on a PC with a random generator and see with his eyes that works. I haven't to waste time to show how the numFormat and Parse float are implemented in Java. Your method returns bad input with 92% faiulure rate, so is clear that the problem is in your solution. – Silverstorm Dec 24 '12 at 12:09
  • `decimalDigits` fails with most inputs. For example, `decimalDigits(1, .9f)` returns 0.89999997615814208984375 instead of .9. – Eric Postpischil Dec 24 '12 at 13:43
  • @Eric Postpischil I wondering how you have compiled the code decimalDigits(1, (float)0.9f)) returns 0.9... fails only if you pass a DOUBLE. Because if you pass a double you have to change also the parse from Float to Double. – Silverstorm Dec 24 '12 at 14:25
  • @Silverstorm: I called `decimalDigits` exactly as shown, with `decimalDigits(1, .9f)`. This passes `.9f` as a float; adding a cast to `float` has no effect (which I tested). In fact, `decimalDigits(1, .9f)` returns 0.89999997615814208984375, which is incorrect because it is not .9. If you see .9 returned, this is likely because you are printing it with default behavior of `System.out.println`, which does not print the exact value. Try printing with `System.out.format("%.99g\n", x);`. This may also not print the exact value, but it will show it accurately enough that you can see it is not .9. – Eric Postpischil Dec 24 '12 at 18:10
  • @EricPostpischil I have tested it. As I have said If you try to call decimalDigits(1, (float)0.9f) you can get the right value, but if you call decimalDigits(1, 0.9f) returns 0.89999997615814208984375. So is not the same thing. – Silverstorm Dec 25 '12 at 10:14
  • Please show the complete executable code for the former. – Eric Postpischil Dec 25 '12 at 11:07
  • If you write float x=decimalDigits(1, 0.8999999761581421); System.out.println("Value is: "+x); Print "Value is 0.9" OR float x=decimalDigits(1, (float)0.9f); System.out.println("Value is: "+x); Print "Value is 0.9" That is the same code written in different way. But if you write without any cast double x=decimalDigits(1, 0.9f); System.out.println("Value is: "+x); Print "Value is 0.89999997615814208984375" So the cast is necessary, if you change all parameters of the method from float to double this not happen. – Silverstorm Dec 25 '12 at 14:10
  • @EricPostpischil Effectively as have you said System.out.format unlike System.out.println shows extended value output, but this doesn't matter, the important is that the approximation doesn't change the values wrongly, out of the needed decimals precision, what is asked for is a method that APPROXIMATES the number of decimal digits to avoid to output a huge number of decimals and this is what the method does. The differences between printf and println shows that there is no precision also in the java base output. If you need a high precision you don't use approximation and you don't use float. – Silverstorm Dec 25 '12 at 14:50
  • Are you retracting your statement that `decimalDigits(1, (float)0.9f)` and `decimalDigits(1, 0.9f)` return different values? – Eric Postpischil Dec 25 '12 at 15:15
  • @EricPostpischil Strangely I cannot reproduce the issue with (0.9f,1) without cast, so I cannot insist until further test. However If you write directly the value you should verify that the passed value is seen as float and not as double. Are you sure that if you write (0.9,1) is seen as float and not as double? In more situations this doesn't even compile trowing an error about the argument. But what is the point? If you pass float and assign the result to a float the method do what is intended to do, either if you pass double and convert the method to double as said. So what is the problem? – Silverstorm Dec 25 '12 at 17:38
0

It is impossible for a routine that returns a float or a double to return correctly rounded values such as 36.59 or 36.6 because these values are not representable in binary floating-point. Binary floating-point can only return values that are close, such as 36.60000000000000142108547152020037174224853515625. There is no bit pattern in binary floating-point that represents the value 36.6.

If you want decimal values, you must use a decimal radix, such as DecimalFormat or BigDecimal.

Eric Postpischil
  • 141,624
  • 10
  • 138
  • 247
user207421
  • 289,834
  • 37
  • 266
  • 440
  • 1
    +1 for the only correct answer. – Louis Wasserman Dec 24 '12 at 01:47
  • 2
    My solution works and answers the question... you can try using Eclipse. Despite floating point variables are saved has binary, you can manipulate float with numFormat and meet the problem easily. – Silverstorm Dec 24 '12 at 03:30
  • @Silverstorm So please explain its enormous failure rate as documented [here](http://stackoverflow.com/a/12684082/207421), and then please explain exactly how a binary fraction can represent a decimal fraction to an exact number of decimal places, and then explain why using a decimal radix is the wrong answer. – user207421 Dec 24 '12 at 04:29
  • @EJP Try to use the method of my answer on Eclipse and explain what doesn't work. I have tested and the output is correct. You can also use DecimalFormat or BigDecimal but this doesn't answer the specific question, mean "change your code you cannot use float to meet the problem" – Silverstorm Dec 24 '12 at 04:44
  • @Silverstorm I have already explained why it cannot possibly work. The onus is now on you to explain the negative results of my tests. A few positives mean nothing, especially as you haven't documented your test procedure. Advocates of (x/10)*10 always downvote this answer whenever I give it, but none of them has yet explained my test results. You also have yet to explain why using a decimal radix is wrong, or what Eclipse has to do with it. – user207421 Dec 24 '12 at 04:48
  • @EJP In all my test the output is always right. I haven't to explain why YOUR solution doesn't works, if you say that MY solution doesn't work, you have to give me a sample of float that cannot be converted by my method. If you get a wrong output with your method, mean that you have made some error doesn't mean that there isn't any solution and all solutions are wrong. – Silverstorm Dec 24 '12 at 04:58
  • @Silverstorm In all what tests? You will haven't disclosed your testing methodology; you still haven't explained how it works when it can't, and you still haven't explained why using a decimal radix as I have answere here is the wrong answer. And if you at – user207421 Dec 24 '12 at 05:01
  • I have tried to cycle the method in a while(true) with a random generated float fixed to 1,2 or 3 decimal digits and haven't get any wrong converted value. – Silverstorm Dec 24 '12 at 05:04
  • @Silverstorm The linked post already gives 92/100 floats that don't work, and when you go to 3 decimal places it gets worse. As for your alleged 'tests', you still haven't disclosed your methodology. I've disclosed mine. If there is something wrong with it you need to show what. This is called scientific method. Not that I need to prove an analytic truth. Finally, you still haven't explained why using a decimal radix is the wrong answer: this has to be the purpose of all these comments, otherwise you are off topic. – user207421 Dec 24 '12 at 05:10
  • 2
    @EJP Has I have said, your answer is a wrong answer, because assume the problem is unsolvable. Your post is off-topic, you have posted a link to a your method, this prof that you wasn't able to meet the problem, NOT that there isn't any possible solution. I have tried the solution and works, My question was accepted, and the class NumberFormat with the parsing approx the value rightly so... what is your problem?? If you are particularly curious to test and aren't convinced, could try by yourself to test MY METHOD and report any problem that you find relative to the proposed solutions. – Silverstorm Dec 24 '12 at 05:38
  • @Silverstorm I will help you by restating. It *is* unsolvable. No method that returns a float or double can possibly solve this problem, for the reasons I have given. I have not 'assumed' but *proven* it, both analytically and experimentally. You *still* haven't published your testing methodology, but when and if you do so, I will refute that too. Still waiting. For your further assistance the first value that fails is 0.01. – user207421 Dec 24 '12 at 05:38
  • 1
    0.01 becomes 0.0 that is right... yet tried a minute ago if you pass float the method works, – AndreaF Dec 24 '12 at 05:42
  • 2
    @EJP Tested if I pass float 0.01 with 1 decimal digit returns float 0.0 that is right, If I pass float 0.05 returns float 0.1 that is right. etc. – Silverstorm Dec 24 '12 at 05:53
  • @Silverstorm A handful of correct results in the face of an analytic disproof and an established experimental 92% failure rate prove nothing. The results of your alleged tests are 100% worthless until you publish your methodology. You've had plenty of chances to engage in a scientific discussion. Until you do, further comment is unnecessary. – user207421 Dec 24 '12 at 06:14
  • @EJP As I have said YOUR method has a 92% failure rate, this mean almost totally wrong, is simple try to run a while(true) with random float generator and compare the output result of my method. So are unnecessary your polemics. If you are able to find a failure input that effectively doesn't work with MY method you are welcome, otherwise you are simply spamming that your method doesn't works. – Silverstorm Dec 24 '12 at 12:15
  • @EJP - Sun has gone to great lengths to hide the fact that floating-point numbers cannot represent all values: `Float.toString()` uses `sun.misc.FloatingDecimal` to convert values with "only as many [...] digits as are needed to uniquely distinguish the argument value from adjacent values". Which means that it will be very hard to convince someone who says "I printed the value, so I'm right." But +1 for fighting the good fight (and -1 to the other answer, as well as this all-to-often repeated question). – kdgregory Dec 24 '12 at 14:43
-1

Possible pseudo code is below:

public class test {

    /**
     * @param args
     */
    public static void main(String[] args) {
        System.out.println(decimalDigits(10.09872, 4));
    }

     static double decimalDigits(double value, int n)
    {
        double decimal = value - ((int) value);
        System.out.println(decimal);

        double short_decimal = 0;
        for(int i = 0; i < n; i++)
        {
            /* current digit on decimal */
            decimal = decimal * 10;
            System.out.println(decimal);
            short_decimal += (Math.pow(10, n - i - 1) * (int)decimal);

            /* find further */
            decimal = decimal - (int)decimal;
        }

        return (int)value + (double)(short_decimal / Math.pow(10, n));
    }

}
Shivam
  • 2,076
  • 16
  • 27
  • This technique does not work. -1 – user207421 Dec 24 '12 at 02:37
  • 'Seems like it is working' isn't the same as 'working'. Binary fractions cannot represent decimal fractions to a specific number of decimal places except in a small number of cases where they coincide. – user207421 Dec 24 '12 at 04:31
  • The above technique works. You are just over complicating the stuff. This is not about how floating point is representing internally, he just wanted a method that could reduce the decimal places. – Shivam Dec 24 '12 at 18:12