Round a double to 3 significant figures
double d = ...;
BigDecimal bd = new BigDecimal(d);
bd = bd.round(new MathContext(3));
double rounded = bd.doubleValue();
If you want to do it by hand:
import java.lang.Math;
public class SigDig {
public static void main(String[] args) {
System.out.println(" -123.456 rounded up to 2 sig figures is " + sigDigRounder(-123.456, 2, 1));
System.out.println(" -0.03394 rounded down to 3 sig figures is " + sigDigRounder(-0.03394, 3, -1));
System.out.println(" 474 rounded up to 2 sig figures is " + sigDigRounder(474, 2, 1));
System.out.println("3004001 rounded down to 4 sig figures is " + sigDigRounder(3004001, 4, -1));
}
public static double sigDigRounder(double value, int nSigDig, int dir) {
double intermediate = value/Math.pow(10,Math.floor(Math.log10(Math.abs(value)))-(nSigDig-1));
if(dir > 0) intermediate = Math.ceil(intermediate);
else if (dir< 0) intermediate = Math.floor(intermediate);
else intermediate = Math.round(intermediate);
double result = intermediate * Math.pow(10,Math.floor(Math.log10(Math.abs(value)))-(nSigDig-1));
return(result);
}
}
The above method rounds a double to a desired number of significant figures, handles negative numbers, and can be explicitly told to round up or down
There's nothing wrong with the answer given by Sean Owen (https://stackoverflow.com/a/7548871/274677). However depending on your use case you might want to arrive at a String representation. In that case, IMO it is best to convert while still in BigDecimal space using:
bd.toPlainString();
... if that's your use case then you might be frustrated that the code adapted from Owen's answer will produce the following:
d = 0.99, significantDigits = 3 ==> 0.99
... instead of the strictly more accurate 0.990
.
If such things are important in your use case then I suggest the following adaptation to Owen's answer ; you can obviously also return the BigDecimal itself instead of calling toPlainString()
— I just provide it this way for completeness.
public static String setSignificanDigits(double value, int significantDigits) {
if (significantDigits < 0) throw new IllegalArgumentException();
// this is more precise than simply doing "new BigDecimal(value);"
BigDecimal bd = new BigDecimal(value, MathContext.DECIMAL64);
bd = bd.round(new MathContext(significantDigits, RoundingMode.HALF_UP));
final int precision = bd.precision();
if (precision < significantDigits)
bd = bd.setScale(bd.scale() + (significantDigits-precision));
return bd.toPlainString();
}