What's the best practice to round a float to 2 decimals?

201,820

Solution 1

I was working with statistics in Java 2 years ago and I still got the codes of a function that allows you to round a number to the number of decimals that you want. Now you need two, but maybe you would like to try with 3 to compare results, and this function gives you this freedom.

/**
* Round to certain number of decimals
* 
* @param d
* @param decimalPlace
* @return
*/
public static float round(float d, int decimalPlace) {
    BigDecimal bd = new BigDecimal(Float.toString(d));
    bd = bd.setScale(decimalPlace, BigDecimal.ROUND_HALF_UP);
    return bd.floatValue();
}

You need to decide if you want to round up or down. In my sample code I am rounding up.

Hope it helps.

EDIT

If you want to preserve the number of decimals when they are zero (I guess it is just for displaying to the user) you just have to change the function type from float to BigDecimal, like this:

public static BigDecimal round(float d, int decimalPlace) {
    BigDecimal bd = new BigDecimal(Float.toString(d));
    bd = bd.setScale(decimalPlace, BigDecimal.ROUND_HALF_UP);       
    return bd;
}

And then call the function this way:

float x = 2.3f;
BigDecimal result;
result=round(x,2);
System.out.println(result);

This will print:

2.30

Solution 2

Let's test 3 methods:
1)

public static double round1(double value, int scale) {
    return Math.round(value * Math.pow(10, scale)) / Math.pow(10, scale);
}

2)

public static float round2(float number, int scale) {
    int pow = 10;
    for (int i = 1; i < scale; i++)
        pow *= 10;
    float tmp = number * pow;
    return ( (float) ( (int) ((tmp - (int) tmp) >= 0.5f ? tmp + 1 : tmp) ) ) / pow;
}

3)

public static float round3(float d, int decimalPlace) {
    return BigDecimal.valueOf(d).setScale(decimalPlace, BigDecimal.ROUND_HALF_UP).floatValue();
}



Number is 0.23453f
We'll test 100,000 iterations each method.

Results:
Time 1 - 18 ms
Time 2 - 1 ms
Time 3 - 378 ms


Tested on laptop
Intel i3-3310M CPU 2.4GHz

Solution 3

double roundTwoDecimals(double d) {
  DecimalFormat twoDForm = new DecimalFormat("#.##");
  return Double.valueOf(twoDForm.format(d));
}

Solution 4

Here is a shorter implementation comparing to @Jav_Rock's

   /**
     * Round to certain number of decimals
     * 
     * @param d
     * @param decimalPlace the numbers of decimals
     * @return
     */

    public static float round(float d, int decimalPlace) {
         return BigDecimal.valueOf(d).setScale(decimalPlace,BigDecimal.ROUND_HALF_UP).floatValue();
    }



    System.out.println(round(2.345f,2));//two decimal digits, //2.35

Solution 5

I've tried to support the -ve values for @Ivan Stin excellent 2nd method. (Major credit goes to @Ivan Stin for his method)

public static float round(float value, int scale) {
    int pow = 10;
    for (int i = 1; i < scale; i++) {
        pow *= 10;
    }
    float tmp = value * pow;
    float tmpSub = tmp - (int) tmp;

    return ( (float) ( (int) (
            value >= 0
            ? (tmpSub >= 0.5f ? tmp + 1 : tmp)
            : (tmpSub >= -0.5f ? tmp : tmp - 1)
            ) ) ) / pow;

    // Below will only handles +ve values
    // return ( (float) ( (int) ((tmp - (int) tmp) >= 0.5f ? tmp + 1 : tmp) ) ) / pow;
}

Below are the tests cases I've tried. Please let me know if this is not addressing any other cases.

@Test
public void testFloatRound() {
    // +ve values
    Assert.assertEquals(0F, NumberUtils.round(0F), 0);
    Assert.assertEquals(1F, NumberUtils.round(1F), 0);
    Assert.assertEquals(23.46F, NumberUtils.round(23.4567F), 0);
    Assert.assertEquals(23.45F, NumberUtils.round(23.4547F), 0D);
    Assert.assertEquals(1.00F, NumberUtils.round(0.49999999999999994F + 0.5F), 0);
    Assert.assertEquals(123.12F, NumberUtils.round(123.123F), 0);
    Assert.assertEquals(0.12F, NumberUtils.round(0.123F), 0);
    Assert.assertEquals(0.55F, NumberUtils.round(0.55F), 0);
    Assert.assertEquals(0.55F, NumberUtils.round(0.554F), 0);
    Assert.assertEquals(0.56F, NumberUtils.round(0.556F), 0);
    Assert.assertEquals(123.13F, NumberUtils.round(123.126F), 0);
    Assert.assertEquals(123.15F, NumberUtils.round(123.15F), 0);
    Assert.assertEquals(123.17F, NumberUtils.round(123.1666F), 0);
    Assert.assertEquals(123.46F, NumberUtils.round(123.4567F), 0);
    Assert.assertEquals(123.87F, NumberUtils.round(123.8711F), 0);
    Assert.assertEquals(123.15F, NumberUtils.round(123.15123F), 0);
    Assert.assertEquals(123.89F, NumberUtils.round(123.8909F), 0);
    Assert.assertEquals(124.00F, NumberUtils.round(123.9999F), 0);
    Assert.assertEquals(123.70F, NumberUtils.round(123.7F), 0);
    Assert.assertEquals(123.56F, NumberUtils.round(123.555F), 0);
    Assert.assertEquals(123.00F, NumberUtils.round(123.00F), 0);
    Assert.assertEquals(123.50F, NumberUtils.round(123.50F), 0);
    Assert.assertEquals(123.93F, NumberUtils.round(123.93F), 0);
    Assert.assertEquals(123.93F, NumberUtils.round(123.9312F), 0);
    Assert.assertEquals(123.94F, NumberUtils.round(123.9351F), 0);
    Assert.assertEquals(123.94F, NumberUtils.round(123.9350F), 0);
    Assert.assertEquals(123.94F, NumberUtils.round(123.93501F), 0);
    Assert.assertEquals(99.99F, NumberUtils.round(99.99F), 0);
    Assert.assertEquals(100.00F, NumberUtils.round(99.999F), 0);
    Assert.assertEquals(100.00F, NumberUtils.round(99.9999F), 0);

    // -ve values
    Assert.assertEquals(-123.94F, NumberUtils.round(-123.93501F), 0);
    Assert.assertEquals(-123.00F, NumberUtils.round(-123.001F), 0);
    Assert.assertEquals(-0.94F, NumberUtils.round(-0.93501F), 0);
    Assert.assertEquals(-1F, NumberUtils.round(-1F), 0);
    Assert.assertEquals(-0.50F, NumberUtils.round(-0.50F), 0);
    Assert.assertEquals(-0.55F, NumberUtils.round(-0.55F), 0);
    Assert.assertEquals(-0.55F, NumberUtils.round(-0.554F), 0);
    Assert.assertEquals(-0.56F, NumberUtils.round(-0.556F), 0);
    Assert.assertEquals(-0.12F, NumberUtils.round(-0.1234F), 0);
    Assert.assertEquals(-0.12F, NumberUtils.round(-0.123456789F), 0);
    Assert.assertEquals(-0.13F, NumberUtils.round(-0.129F), 0);
    Assert.assertEquals(-99.99F, NumberUtils.round(-99.99F), 0);
    Assert.assertEquals(-100.00F, NumberUtils.round(-99.999F), 0);
    Assert.assertEquals(-100.00F, NumberUtils.round(-99.9999F), 0);
}
Share:
201,820
vgonisanz
Author by

vgonisanz

Victor Goñi received his M.S. degree in Telecommunication Engeneering from the Public University of Navarra, Pamplona, Spain, in 2010. He spent an internship course at the Monterrey Institute of Technology and Higher Education, Mexico, during 2009, as a complement of his Engineer degree. He wrote his final Master Thesis at Vicomtech-IK4, San Sebastian, Spain, under the title of "Peripheral Evaluation for Virtual Environment Control", which received an A grade. Since then, he is working in the 3D Animation and Interactive Virtual Environments Department of Vicomtech-IK4 as a research assistant. He has developed a deep interest in science, programming and scientific research, especially in algorithm efficiency, real-time complex calculation and optimization. He is also interested in graphic engines and sensor data filtering and processing for electronic devices. He is specialized in code developement on mobile platforms. Also he is interested in resurrect tiranosaurios rex. My goal to achieve is to be a developer/architect that writes C++ like musicians compose music and songs.

Updated on July 17, 2022

Comments

  • vgonisanz
    vgonisanz almost 2 years

    I'm using eclipse + Android SDK.

    I need to round a float value to 2 decimals. I usually use the next "trick" using Math library.

    float accelerometerX = accelerometerX * 100;
        accelerometerX = round(accelerometerX);
        Log.d("Test","" + accelerometerX/100);
    

    But I feel it is not the best way to do it.

    Is there a library to do these type of operations?

    • Jave
      Jave over 12 years
      You can take a look at this question: stackoverflow.com/questions/153724/…
    • L7ColWinters
      L7ColWinters over 12 years
      or you could find the number after the decimal, cast it to int, and right bit shift. Basically what round would do.
  • vgonisanz
    vgonisanz over 12 years
    I'm trying to use that function with float, but throw exceptions
  • vgonisanz
    vgonisanz over 12 years
    But now, the float show only 1 decimal if the second is 0. Do you know how to show always sign and all (2) decimals?? Example: 2.1234 --> 2.12 but 2.1 --> 2.1 but no 2.10
  • marienke
    marienke almost 11 years
    How would you round to 2 decimal places in Android?
  • akohout
    akohout over 10 years
    The method is designed for Doubles. I added the same function for Floats.
  • azerafati
    azerafati almost 10 years
    I like to keep it short new BigDecimal(String.valueOf(double)).setScale(yourScale, BigDecimal.ROUND_HALF_UP);
  • Sandeep16
    Sandeep16 over 9 years
    we Have to use import java.text.DecimalFormat; other wise it throws Exception...
  • Teepeemm
    Teepeemm over 9 years
    This is a lot of extra fluff for what is essentially Shadow's answer of DecimalFormat.format(double).
  • Ranjithkumar
    Ranjithkumar about 8 years
    this is working for me.. you need to change like this float roundTwoDecimals(float d) { DecimalFormat twoDForm = new DecimalFormat("#.##"); return Float.valueOf(twoDForm.format(d)); }
  • hamham
    hamham almost 8 years
    I implemented this in an Android game I am working on, creating a new BigDecimal instance multiple times each frame accounted for 99.9% of my memory allocations, so something to watch out for. In my opinion @Evan Stin's method 2 is the best answer, it's faster and doesn't leave anything behind.
  • Jav_Rock
    Jav_Rock almost 8 years
    Yes, probably it is the best answer, but 4 years later, I think the guy who asked does not need that anymore :)
  • fIwJlxSzApHEZIl
    fIwJlxSzApHEZIl almost 7 years
    What does this rounding function look like if it accepted a BigDecimal as the argument instead of a float or double?
  • Lym Zoy
    Lym Zoy over 6 years
    The second one does not work for negative numbers. E.g. round(-10.26f, 1) returns -10.2 rather than -10.3.
  • manikanta
    manikanta over 6 years
    @LymZoy I've fixed the -ve numbers issue. See my answer stackoverflow.com/a/45772416/340290
  • gil.fernandes
    gil.fernandes over 6 years
    Why not use a variable for the powered number in the first example?: private static double round(double average, int scale) { double pow = Math.pow(10, scale); return Math.round(average * pow) / pow; }
  • Ivan Stin
    Ivan Stin over 6 years
    @gil.fernandes, good note
  • Blackpanther0001
    Blackpanther0001 almost 6 years
    Use bd.setScale(decimalPlace, RoundingMode.UP); instead of bd.setScale(decimalPlace, BigDecimal.ROUND_HALF_UP); --> latter is deprecated
  • Abigail La'Fay
    Abigail La'Fay almost 5 years
    As @IvanStin tested this is the fastest and the best instant solution. Thank you!
  • Sergio
    Sergio about 4 years
    does not work with negative values. If you want to fix that change the formula to: ((int) ((value + (value >= 0 ? 1 : -1) * 0.005f) * 100)) / 100f;
  • Hasen
    Hasen over 3 years
    double d? Might wanna rethink that.