Related
How can I get the correct answer when I need to use BigDecimal without losing precision.
BigDecimal a = new BigDecimal(0.5);
BigDecimal b = new BigDecimal(30);
BigDecimal c = new BigDecimal(18000);
a.divide(b).multiply(c);
How could I get the exact 300 in this case?
Thanks!
You can use the MathContext parameter in the divide method for this.
For example, a.divide(b, MathContext.DECIMAL128).multiply(c); will give you the precision you need (with an error of magnitude 1e-32). If you do not want to lose any precision, you can use MathContext.UNLIMITED, but this will result in a non-terminating decimal expansion.
In your case specifically, you can also try to rewrite the equation to prevent any rounding from happening: a / b * c = c / b * a.
This question already has answers here:
How to round a number to n decimal places in Java
(39 answers)
Closed 8 years ago.
If the value is 200.3456, it should be formatted to 200.34.
If it is 200, then it should be 200.00.
Here's an utility that rounds (instead of truncating) a double to specified number of decimal places.
For example:
round(200.3456, 2); // returns 200.35
Original version; watch out with this
public static double round(double value, int places) {
if (places < 0) throw new IllegalArgumentException();
long factor = (long) Math.pow(10, places);
value = value * factor;
long tmp = Math.round(value);
return (double) tmp / factor;
}
This breaks down badly in corner cases with either a very high number of decimal places (e.g. round(1000.0d, 17)) or large integer part (e.g. round(90080070060.1d, 9)). Thanks to Sloin for pointing this out.
I've been using the above to round "not-too-big" doubles to 2 or 3 decimal places happily for years (for example to clean up time in seconds for logging purposes: 27.987654321987 -> 27.99). But I guess it's best to avoid it, since more reliable ways are readily available, with cleaner code too.
So, use this instead
(Adapted from this answer by Louis Wasserman and this one by Sean Owen.)
public static double round(double value, int places) {
if (places < 0) throw new IllegalArgumentException();
BigDecimal bd = BigDecimal.valueOf(value);
bd = bd.setScale(places, RoundingMode.HALF_UP);
return bd.doubleValue();
}
Note that HALF_UP is the rounding mode "commonly taught at school". Peruse the RoundingMode documentation, if you suspect you need something else such as Bankers’ Rounding.
Of course, if you prefer, you can inline the above into a one-liner:
new BigDecimal(value).setScale(places, RoundingMode.HALF_UP).doubleValue()
And in every case
Always remember that floating point representations using float and double are inexact.
For example, consider these expressions:
999199.1231231235 == 999199.1231231236 // true
1.03 - 0.41 // 0.6200000000000001
For exactness, you want to use BigDecimal. And while at it, use the constructor that takes a String, never the one taking double. For instance, try executing this:
System.out.println(new BigDecimal(1.03).subtract(new BigDecimal(0.41)));
System.out.println(new BigDecimal("1.03").subtract(new BigDecimal("0.41")));
Some excellent further reading on the topic:
Item 48: "Avoid float and double if exact answers are required" in Effective Java (2nd ed) by Joshua Bloch
What Every Programmer Should Know About Floating-Point Arithmetic
If you wanted String formatting instead of (or in addition to) strictly rounding numbers, see the other answers.
Specifically, note that round(200, 0) returns 200.0. If you want to output "200.00", you should first round and then format the result for output (which is perfectly explained in Jesper's answer).
If you just want to print a double with two digits after the decimal point, use something like this:
double value = 200.3456;
System.out.printf("Value: %.2f", value);
If you want to have the result in a String instead of being printed to the console, use String.format() with the same arguments:
String result = String.format("%.2f", value);
Or use class DecimalFormat:
DecimalFormat df = new DecimalFormat("####0.00");
System.out.println("Value: " + df.format(value));
I think this is easier:
double time = 200.3456;
DecimalFormat df = new DecimalFormat("#.##");
time = Double.valueOf(df.format(time));
System.out.println(time); // 200.35
Note that this will actually do the rounding for you, not just formatting.
The easiest way, would be to do a trick like this;
double val = ....;
val = val*100;
val = Math.round(val);
val = val /100;
if val starts at 200.3456 then it goes to 20034.56 then it gets rounded to 20035 then we divide it to get 200.34.
if you wanted to always round down we could always truncate by casting to an int:
double val = ....;
val = val*100;
val = (double)((int) val);
val = val /100;
This technique will work for most cases because for very large doubles (positive or negative) it may overflow. but if you know that your values will be in an appropriate range then this should work for you.
Please use Apache commons math:
Precision.round(10.4567, 2)
function Double round2(Double val) {
return new BigDecimal(val.toString()).setScale(2,RoundingMode.HALF_UP).doubleValue();
}
Note the toString()!!!!
This is because BigDecimal converts the exact binary form of the double!!!
These are the various suggested methods and their fail cases.
// Always Good!
new BigDecimal(val.toString()).setScale(2,RoundingMode.HALF_UP).doubleValue()
Double val = 260.775d; //EXPECTED 260.78
260.77 - WRONG - new BigDecimal(val).setScale(2,RoundingMode.HALF_UP).doubleValue()
Double val = 260.775d; //EXPECTED 260.78
260.77 - TRY AGAIN - Math.round(val * 100.d) / 100.0d
Double val = 256.025d; //EXPECTED 256.03d
256.02 - OOPS - new DecimalFormat("0.00").format(val)
// By default use half even, works if you change mode to half_up
Double val = 256.025d; //EXPECTED 256.03d
256.02 - FAIL - (int)(val * 100 + 0.5) / 100.0;
double value= 200.3456;
DecimalFormat df = new DecimalFormat("0.00");
System.out.println(df.format(value));
If you really want the same double, but rounded in the way you want you can use BigDecimal, for example
new BigDecimal(myValue).setScale(2, RoundingMode.HALF_UP).doubleValue();
double d = 28786.079999999998;
String str = String.format("%1.2f", d);
d = Double.valueOf(str);
For two rounding digits. Very simple and you are basically updating the variable instead of just display purposes which DecimalFormat does.
x = Math.floor(x * 100) / 100;
Rounding a double is usually not what one wants. Instead, use String.format() to represent it in the desired format.
In your question, it seems that you want to avoid rounding the numbers as well? I think .format() will round the numbers using half-up, afaik?
so if you want to round, 200.3456 should be 200.35 for a precision of 2. but in your case, if you just want the first 2 and then discard the rest?
You could multiply it by 100 and then cast to an int (or taking the floor of the number), before dividing by 100 again.
200.3456 * 100 = 20034.56;
(int) 20034.56 = 20034;
20034/100.0 = 200.34;
You might have issues with really really big numbers close to the boundary though. In which case converting to a string and substring'ing it would work just as easily.
value = (int)(value * 100 + 0.5) / 100.0;
I have a crypto portfolio made, but the long percentage numbers or the "E"s instead of big numbers really tear up the design, like in this example:
How to fix them (round up to 2 decimal digits, remove E, remove .0 from 5)?
Try this:
Math.round(number * 100.0) / 100.0;
You can find plenty of other ways if you search.
Using the Double constructor, you can turn all of those strings into doubles.
String someNumberString = "1.2E5";
Double dNum = new Double(someNumberString);
You can do rounding as such:
double rounded = (Math.round(dNum * ROUND_TO_OFFSET) / ROUND_TO_OFFSET)
where ROUND_TO_OFFSET specifies the digit to round to. E.g. ROUND_TO_OFFSET= 100 rounds to the nearest 0.01. This has a disadvantage though. Math.round returns a long, so if your dNum * ROUND_TO_OFFSET produces a number that is larger or smaller than a long can hold, it will not work correctly.
You can format any double into a nice, easy to read format with the DecimalFormat class.
DecimalFormat decimalFormat = new DecimalFormat("#,##0.#####");
String formattedDouble = decimalFormat.format(someDouble);
This allows you to easily convert double values to strings that look like this:
1,234,567.89012
If you need more precision or numbers larger than what Long.MAX_VALUE can handle, you should checkout java's BigDecimal class.
BigDecimal provides it's own round method that allows you to specify what to round to. You can still use the DecimalFormat class by calling BigDecimal's doubleValue() function.
I have a line of code that adds together a few doubles and it give a result of infinity, while the double are all just small but long decimals
a = -1.536709757154733E308
b = 2.102331119338414E156
c = 0.2
d = a - b + (a * c)
d = -1.536709757154733E308 - 2.102331119338414E156 + (-1.536709757154733E308 * 0.2)
d = -infinity
Does anyone know why this is happening? Any help would be much appreciated
PS it is not feasible to use Big Decimals in this code and due to the random 'a' and 'b' change on each run
Overflow is occurring. The magnitude of the result of the expression a - b + (a * c) is greater than the largest possible double, Double.MAX_VALUE, or 1.7976931348623157E308. When this occurs, the result is an infinity. Because a was negative, this results in -Infinity.
You can see this if you decrease the exponents of a and b by 1 to see what the result would be:
-1.8440517085856795E307
Increasing the exponent by 1 again in the result would yield a magnitude larger than the maximum possible magnitude.
You can still use BigDecimals.
BigDecimal a = new BigDecimal(-1.536709757154733E308);
BigDecimal b = new BigDecimal(2.102331119338414E156);
BigDecimal c = new BigDecimal(0.2);
System.out.println(a.add(b).add(a.multiply(c)));
That decimal point is deceiving you. To see how large that double value actually is, use the NumberFormat as
double d = 2.102331119338414E156;
System.out.println(NumberFormat.getNumberInstance().format(d));
Output :
2,102,331,119,338,414,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000
Hence, switching to a BigDecimal should help. If you're generating the a and b values randomly, take a look at Generating random BigDecimal value from given range.
So, with the scientific notation and large numbers represented out of the way, let's look at a simplified form.
a = -1.5E308 // round down*
b = 2.1E156
c = 0.2
d = a - b + (a * c)
d = -1.5E308 - 2.1E156 + (-1.5E308 * 0.2)
d = -1.5E308 - {insignificant*} + (-0.3E308)
d = -1.8E308 // under normal math ..
.. BUT, 64-bit IEEE 754 Double's - which is the format that double is stored in - have a range of ~[-1.79E308, 1.79E308] as only so much information can be stored in 64 bits.
The result of -1.8E308 is outside of this range. Hence it is represented as (negative) Infinity.
* Note the initial round down and throwing away the insignificant value - this the result is guaranteed to be < -1.8E308, although feel free to work it out to more precision. The point is this is outside the representable range of a double.
Using a BigDecimal works because (and it doesn't matter if the input is "random") because it doesn't have this range limit.
At work, we found a problem when trying to divide a large number by 1000. This number came from the database.
Say I have this method:
private static BigDecimal divideBy1000(BigDecimal dividendo) {
if (dividendo == null) return null;
return dividendo.divide(BigDecimal.valueOf(1000), RoundingMode.HALF_UP);
}
When I make the following call
divideBy1000(new BigDecimal("176100000"))
I receive the expected value of 176100. But if I try the line below
divideBy1000(new BigDecimal("1761e+5"))
I receive the value 200000. Why this occurs? Both numbers are the same with different representation and the latest is what I receive from database. I understand that, somehow, the JVM is dividing the number 1761 by 1000, rounding up and filling with 0's at the end.
What is the best way to avoid this kind of behavior? Keep in mind that the original number is not controlled by me.
As specified in javadoc, a BigDecimal is defined by an integer value and a scale.
The value of the number represented by the BigDecimal is therefore
(unscaledValue × 10^(-scale)).
So BigDecimal("1761e+5") has scale -5 and BigDecimal(176100000) has scale 0.
The division of the two BigDecimal is done using the -5 and 0 scales respectively because the scales are not specified when dividing. The divide documentation explains why the results are different.
divide
public BigDecimal divide(BigDecimal divisor)
Returns a BigDecimal whose value is (this / divisor), and whose preferred scale is (this.scale() - divisor.scale()); if the exact quotient cannot be represented (because it has a non-terminating decimal expansion) an ArithmeticException is thrown.
Parameters:
divisor - value by which this BigDecimal is to be divided.
Returns:
this / divisor
Throws:
ArithmeticException — if the exact quotient does not have a terminating decimal expansion
Since:
1.5
If you specify a scale when dividing, e.g. dividendo.divide(BigDecimal.valueOf(1000), 0, RoundingMode.HALF_UP) you will get the same result.
The expressions new BigDecimal("176100000") and new BigDecimal("1761e+5") are not equal. BigDecimal keeps track of both value, and precision.
BigDecimal("176100000") has 9 digits of precision and is represented internally as the BigInteger("176100000"), multiplied by 1. BigDecimal("1761e+5") has 4 digits of precision and is represented internally as the BigInteger("1761"), multiplied by 100000.
When you a divide a BigDecimal by a value, the result respects the digits of precision, resulting in different outputs for seemingly equal values.
for your division with BigDecimal.
dividendo.divide(divisor,2,RoundingMode.CEILING)//00.00 nothing for up and nothing for down
in this operation have a precision for two decimals.
To avoid this kind of problems in Java when dividing by powers of 10 you have a much efficient and precise approach:
dividendo.movePointLeft(3)
Yeah, that's kind of issue what you're experimenting. If I may, in a situation where you only have exponental numbers, you should cast them and then use your method. See what I suggest is this bit of code down there:
long longValue = Double.valueOf("1761e+5").longValue();
BigDecimal value= new BigDecimal(longValue);
Use it in a method which would convert those string into a new BigDecimal and return this BigDecimal value. Then you can use those returned values with divideBy1000.That should clear any issue you're having.
If you have a lot of those, what you can do also in store those BigDecimal in a data structure like a list. Then use a foreach loop in which you apply divideBy1000 and each new value would be stored in a different list. Then you would just have to access this list to have your new set of values !
Hope it helps :)
Try using round().
private static BigDecimal divideBy1000(BigDecimal dividendo) {
if (dividendo == null) return null;
return dividendo.divide(BigDecimal.valueOf(1000)).round(new MathContext(4, RoundingMode.HALF_UP));
}
public static void main(String []args){
BigDecimal bigD = new BigDecimal("1761e5");
BigDecimal bigDr = divideBy1000(bigD);
System.out.println(bigDr);
}
The new MathContext(4, RoundingMode.HALF_UP)) line returns the division to 4 places.
This produces:
1.761E+5
Which is what you want. (:
Any time you are multiplying a BigDecimal by a power of 10, in this case you are multiplying by 10-3, you can use dividendo.scaleByPowerOfTen(power) which only modifies the scale of the BigDecimal object and side steps any rounding issues, or at least moves them to a later calculation.
The other answers here cover the more general case of dividing by any number.
I want to quote basic concepts for BigDecimal:
A BigDecimal consists of an arbitrary precision integer unscaled value and a 32-bit integer scale.
public class BigDecimal extends Number implements Comparable<BigDecimal> {
// …
private final BigInteger intVal;
private final int scale;
}
That is, BigDecimal number is represented as unscaled integer value * 10^(-scale)
For example, 1.234 = 1234 * 10^(-3). So, precision is 4, scale is 3.
Please refer to basic concept in here.
For the former:
BigDecimal bd1 = new BigDecimal("176100000");
System.out.println(bd1.precision()); // 9
System.out.println(bd1.scale()); // 0
BigDecimal bd2 = BigDecimal.valueOf(1000);
System.out.println(bd2.precision()); // 4
System.out.println(bd2.scale()); // 0
System.out.println(bd1.divide(bd2)); // 176100
System.out.println(bd1.divide(bd2, RoundingMode.HALF_UP)); // 176100
BigDecimal result = bd1.divide(bd2);
System.out.println(result.precision()); // 6
System.out.println(result.scale()); // 0
The new BigDecimal("176100000")'s precision is 9 and scale is 0.
The BigDecimal.valueOf(1000)'s precision is 4 and scale is 0.
(176100000 * 10^0) / (1000 * 10^0) = 176100 * 10^0.
With method public BigDecimal divide(BigDecimal divisor, RoundingMode roundingMode), we have to use the dividend(new BigDecimal("176100000"))'s scale as a scale of returning BigDecimal. In this case, the scale is 0.
Returns a BigDecimal whose value is (this / divisor), and whose scale is this.scale().
As a result, we have BigDecimal number 176100 * 10^0 whose precision is 6 and scale is 0.
The rounding is applied, but the result is integer already, so we just get 176100.
For the latter:
BigDecimal bd1 = new BigDecimal("1761e+5");
System.out.println(bd1.precision()); // 4
System.out.println(bd1.scale()); // -5
BigDecimal bd2 = BigDecimal.valueOf(1000);
System.out.println(bd2.precision()); // 4
System.out.println(bd2.scale()); // 0
System.out.println(bd1.divide(bd2)); // 1.761E+5
System.out.println(bd1.divide(bd2, RoundingMode.HALF_UP)); // 2E+5
BigDecimal result1 = bd1.divide(bd2);
System.out.println(result1.precision()); // 4
System.out.println(result1.scale()); // -2
BigDecimal result2 = bd1.divide(bd2, RoundingMode.HALF_UP);
System.out.println(result2.precision()); // 1
System.out.println(result2.scale()); // -5
The new BigDecimal("1761e+5")'s precision is 4 and scale is -5.
The BigDecimal.valueOf(1000)'s precision is 4 and scale is 0.
(1761 * 10^(-(-5))) / (1000 * 10^0) = 1.761 * 10^(-(-5))
= 1761 * 10^(-(-2)) whose precision is 4 and scale is -2; prints "1.761E+5" using scientific notation of overriden toString.
If we apply rounding, 1.761 * 10^(-(-5)) = 2 * 10^(-(-5)) whose precision is 1 and scale is -5; prints "2E+5" using scientific notation of overriden toString.
I am might be wrong. If you could catch my mistakes, please comment to this answer. I'll correct them.