How to compare BigDecimal, but approximately? - java

I have this code:
BigDecimal d = ...;
if (d.compareTo(Expression.PI) == 0) {
//do something
}
where Expression.PI is pi rounded to 100th decimal.
But I don't need to compare if d is equal to pi with up to 100 decimals, but only let's say up to 20th decimal. To put it other way, how to check if d is approximately equal to pi?
I tried
Expression.PI.setScale(20, RoundingMode.HALF_UP).compareTo(d.setScale(20, RoundingMode.HALF_UP)) == 0;
and
Expression.PI.setScale(20, RoundingMode.HALF_UP).compareTo(d) == 0;
But none of these two seem to work. What am I doing wrong here?

As lucasvw mentioned in the comments, I think you're already doing it correctly and there must be a problem with your 'd' value. Here is a test class that shows the correct result.
import java.math.BigDecimal;
import java.math.RoundingMode;
public class BigDecimalTest {
public static void main(String args[]) {
BigDecimal PI = new BigDecimal("3.14159265358979323846264338327950288419");
BigDecimal otherValue = new BigDecimal("3.14159");
boolean test = PI.setScale(5, RoundingMode.HALF_UP).compareTo(otherValue) == 0;
System.out.println("compareTo: " + test);
}
}

public static final boolean isWithinTolerance(final BigDecimal bigDecimal1, final BigDecimal bigDecimal2, final BigDecimal tolerance)
{
if (bigDecimal1 == null || bigDecimal2 == null || tolerance == null)
{
return false;
}
final BigDecimal diff = bigDecimal1.abs().subtract(bigDecimal2.abs()).abs();
return diff.compareTo(tolerance) < 0;
}

Related

How to get rid of multiple if statements in java?

Is there a better way to write this constructor which has multiple if statements and multiple arguments? I'm a noob to programming so any leads would be helpful.
public Latency(final double full, final double cpuOne, final double cpuTwo, final double cpuThree, final double cpuFour) {
if (full > 10.0 || (full <= 0.0)) {
throw new IllegalArgumentException("Must check the values");
}
this.full = full;
if (cpuOne == 0 && cpuTwo == 0 && cpuThree == 0 && cpuFour == 0) {
throw new IllegalArgumentException("not all can be zero");
} else {
if (cpuOne == 0.5) {
this.cpuOne = full;
} else {
this.cpuOne = cpuOne;
}
if (cpuTwo == 0.5) {
this.cpuTwo = full;
} else {
this.cpuTwo = cpuTwo;
}
if (cpuThree == 0.5) {
this.cpuThree = full;
} else {
this.cpuThree = cpuThree;
}
if (cpuFour == 0.5) {
this.cpuFour = full;
} else {
this.cpuFour = cpuFour;
}
}
}
I think this code doesn't need much of context as it is pretty straight forward.
I found out that we can't use switch statements for type double. How to optimize this?
There are a number of possible ways of refactoring the code that you've written, and there are pros and cons of each one. Here are some ideas.
Idea One - use the conditional operator
You could replace the else block with code that looks like this. This is just effectively a shorter way of writing each of the inner if/else blocks. Many people find this kind of form more readable than a bunch of verbose if/else blocks, but it takes some time to get used to it.
this.cpuOne = cpuOne == 0.5 ? full : cpuOne;
this.cpuTwo = cpuTwo == 0.5 ? full : cpuTwo;
this.cpuThree = cpuThree == 0.5 ? full : cpuThree;
this.cpuFour = cpuFour == 0.5 ? full : cpuFour;
Idea Two - move common functionality to its own method
You could have a method something like this
private static double changeHalfToFull(double value, double full) {
if (value == 0.5) {
return full;
} else {
return value;
}
}
then call it within your constructor, something like this.
this.cpuOne = changeHalfToFull(cpuOne);
this.cpuTwo = changeHalfToFull(cpuTwo);
this.cpuThree = changeHalfToFull(cpuThree);
this.cpuFour = changeHalfToFull(cpuFour);
This has the advantage that the key logic is expressed only once, so it's less error prone than repeating code over and over.
Idea Three - use arrays
You could use an array of four elements in the field that stores these values. You could also use an array for the constructor parameter. This has a huge advantage - it indicates that the four CPU values are somehow all "the same". In other words, there's nothing special about cpuOne compared to cpuTwo, for example. That kind of messaging within your code has real value to someone trying to understand this.
public Latency(final double full, final double[] cpuValues) {
// validation conditions go here ...
this.cpuValues = new double[4];
for (int index = 0; index <= 3; index++) {
if (cpuValues[index] == 0.5) {
this.cpuValues[index] = full;
} else {
this.cpuValues[index] = cpuValues[index];
}
}
}
Or a combination
You could use some combination of all these ideas. For example, you might have something like this, which combines all three of the above ideas.
public Latency(final double full, final double[] cpuValues) {
// validation conditions go here ...
this.cpuValues = new double[4];
for (int index = 0; index <= 3; index++) {
this.cpuValues[index] = changeHalfToFull(cpuValues[index]);
}
}
private static double changeHalfToFull(double value, double full) {
return value == 0.5 ? full : value;
}
There are obviously other possibilities. There is no single correct answer to this question. You need to choose what you're comfortable with, and what makes sense in the larger context of your project.
DRY - Don't Repeat Yourself
Each if is essentially the same. Put it in a separate method and call the method once for each cpu* variable.
public class Latency {
private double full;
private double cpuOne;
private double cpuTwo;
private double cpuThree;
private double cpuFour;
public Latency(final double full,
final double cpuOne,
final double cpuTwo,
final double cpuThree,
final double cpuFour) {
if (full > 10.0 || (full <= 0.0)) {
throw new IllegalArgumentException("Must check the values");
}
this.full = full;
if (cpuOne == 0 && cpuTwo == 0 && cpuThree == 0 && cpuFour == 0) {
throw new IllegalArgumentException("not all can be zero");
}
else {
this.cpuOne = initCpu(cpuOne);
this.cpuTwo = initCpu(cpuTwo);
this.cpuThree = initCpu(cpuThree);
this.cpuFour = initCpu(cpuFour);
}
}
private double initCpu(double cpu) {
return cpu == 0.5 ? full : cpu;
}
public static void main(String[] arg) {
new Latency(9.99, 8.0, 7.0, 6.0, 0.5);
}
}

Round Double datatype upto 3 digits

I am trying to calculate the value a field which represent Interest rate for that I have to round up the value to 3 digits.
Below is code which I am using :
double bigAmt1 = Double.parseDouble(amount);
bigAmt = (intsign*bigAmt1)/div;
bigAmt=Math.round(bigAmt*1000d)/1000d;
amount = 4048500
intsign = 1
div = 6
it returns = 4.048
I need it return = 4.049
if I change the value of amount to 4048600 then it return 4.049 so I think it is rounding up values where last digit after division is greater than 5 but It should be if last digit equal or greater than 5 then It should round up to next digit.
Below is my test class --
package test;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.text.DecimalFormat;
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(divideAndConvertToString1("4048100","6","1"));
//System.out.println("---> 3 places "+Math.round(3.5));
//Math.round(3.7)
/*double value = 12.3457652133;
value =Double.parseDouble(new DecimalFormat("##.####").format(value));
System.out.println("---> 3 places "+value);*/
}
public static String divideAndConvertToString(String amount, String decml, String sign) {
double bigAmt = 0.00;
int div = 0;
double d =0;
if (!isStringEmpty(decml)) {
d = Double.parseDouble(decml);
}
double d1 = Math.pow(10, d);
div = (int)d1;
int intsign = Integer.parseInt(sign);
if (amount != null && !"".equalsIgnoreCase(amount)) {
//BigDecimal bigAmt1 = new BigDecimal(amount);
double bigAmt1 = Double.parseDouble(amount);
bigAmt = (intsign*bigAmt1)/div;
bigAmt=Math.ceil(bigAmt*1000d)/1000d;
//bigAmt = new BigDecimal((intsign*bigAmt1.doubleValue())/div);
return String.valueOf(bigAmt);
}
else {
bigAmt = bigAmt;
}
System.out.println("inside divideAndConvertToString");
return String.valueOf(bigAmt);
}
public static String divideAndConvertToString1(String amount, String decml, String sign) {
BigDecimal bigAmt = null;
int div = 0;
double d =0;
if (!Util.isStringEmpty(decml)) {
d = Double.parseDouble(decml);
}
double d1 = Math.pow(10, d);
div = (int)d1;
int intsign = Integer.parseInt(sign);
if (amount != null && !"".equalsIgnoreCase(amount)) {
BigDecimal bigAmt1 = new BigDecimal(amount);
bigAmt = new BigDecimal((intsign*bigAmt1.doubleValue())/div);
}
else {
bigAmt = new BigDecimal("0");
}
System.out.println("inside divideAndConvertToString1");
return String.valueOf(bigAmt.setScale(3, RoundingMode.CEILING));
//System.out.println(b.setScale(0, RoundingMode.CEILING));
}
public static boolean isStringEmpty(String input) {
if (input == null || input.trim().length() == 0 || "".equalsIgnoreCase(input)) {
return true;
}
return false;
}
}
Math.round should work however I usual do this like this:
bigAmt=Math.floor((bigAmt*1000d)+0.5d)/1000d;
Your problem however lies elsewhere:
bigAmt1 = Double.parseDouble(amount);
bigAmt = (intsign*bigAmt1)/div;
bigAmt=Math.round(bigAmt*1000d)/1000d;
So using your values:
amount = 4048500
intsign = 1
div = 6
bigAmt1 = 4048500;
bigAmt = (1*4048500)/6 = 674750;
bigAmt= round(674750*1000)/1000 = round(674750000)/1000 = 674750;
However in your example You wrote: it returns = 4.048 I need it return = 4.049 So do you have the same div value?
If the div is 1000000 instead then:
bigAmt1 = 4048500;
bigAmt = (1*4048500)/1000000 = 4.048500;
bigAmt= round(4.048500*1000)/1000 = round(4048.500)/1000 = 4.049;
However there is a big problem because floating point might round your 4.048500 number to something like 4.048499999999. It is safer to use integer rounding directly:
1000* ((amount+500)/1000)
1000* ((4048500+500)/1000)
1000* ((4049000 )/1000)
1000* (4049)
4049000
So you add half of the rounding value, divide by rounding value and then multiply by rounding value. All done on integers
Why won't you use Math.ceil(), instead of Math.round(), I think that's what it's for.

Extracting digit values from before and after decimal points in Java

I have 2 values
String latitude = "37.348541";
String longitude = "-121.88627";
I would like to extract like the values as below with out any rounding up the values.
latitude = "37.34";
longitude = "-121.88";
I tried using DecimalFormat.format, but it does some round up and I want to extract an exact value.
You can define a function using String#substring and String#indexOf as shown below:
public class Main {
public static void main(String[] args) {
// Tests
System.out.println(getNumberUptoTwoDecimalPlaces("37.348541"));
System.out.println(getNumberUptoTwoDecimalPlaces("-121.88627"));
System.out.println(getNumberUptoTwoDecimalPlaces("-121.8"));
System.out.println(getNumberUptoTwoDecimalPlaces("-121.88"));
System.out.println(getNumberUptoTwoDecimalPlaces("-121.889"));
}
static String getNumberUptoTwoDecimalPlaces(String number) {
int indexOfPoint = number.indexOf('.');
if (indexOfPoint != -1 && number.length() >= indexOfPoint + 3) {
return number.substring(0, indexOfPoint + 3);
} else {
return number;
}
}
}
Output:
37.34
-121.88
-121.8
-121.88
-121.88
For example:
String latitude = "37.348541";
int i = latitude.indexOf(".");
if(i > 0 && i < latitude.length()-2) latitude = latitude.substring(i, i+2);
You can use the BigDecimal class and the ROUND_DOWN option. So the code could look like this:
BigDecimal number = new BigDecimal("123.13298");
BigDecimal roundedNumber = number.setScale(2, BigDecimal.ROUND_DOWN);
System.out.println(roundedNumber);
Otherwise you can also use the native double and the Math.floor or the Math.ceil method:
double number = 123.13598;
double roundedNumber = (number < 0 ? Math.ceil(number * 100) : Math.floor(number * 100)) / 100;
System.out.println(roundedNumber);

Algorithm to find `balanced number` - the same number of even and odd dividers

We define balanced number as number which has the same number of even and odd dividers e.g (2 and 6 are balanced numbers). I tried to do task for polish SPOJ however I always exceed time.
The task is to find the smallest balance number bigger than given on input.
There is example input:
2 (amount of data set)
1
2
and output should be:
2
6
This is my code:
import java.math.BigDecimal;
import java.util.Scanner;
public class Main {
private static final BigDecimal TWO = new BigDecimal("2");
public static void main(String[] args) throws java.lang.Exception {
Scanner in = new Scanner(System.in);
int numberOfAttempts = in.nextInt();
for (int i = 0; i < numberOfAttempts; i++) {
BigDecimal fromNumber = in.nextBigDecimal();
findBalancedNumber(fromNumber);
}
}
private static boolean isEven(BigDecimal number){
if(number.remainder(new BigDecimal("2")).compareTo(BigDecimal.ZERO) != 0){
return false;
}
return true;
}
private static void findBalancedNumber(BigDecimal fromNumber) {
BigDecimal potentialBalancedNumber = fromNumber.add(BigDecimal.ONE);
while (true) {
int evenDivider = 0;
int oddDivider = 1; //to not start from 1 as divisor, it's always odd and divide potentialBalancedNumber so can start checking divisors from 2
if (isEven(potentialBalancedNumber)) {
evenDivider = 1;
} else {
oddDivider++;
}
for (BigDecimal divider = TWO; (divider.compareTo(potentialBalancedNumber.divide(TWO)) == -1 || divider.compareTo(potentialBalancedNumber.divide(TWO)) == 0); divider = divider.add(BigDecimal.ONE)) {
boolean isDivisor = potentialBalancedNumber.remainder(divider).compareTo(BigDecimal.ZERO) == 0;
if(isDivisor){
boolean isEven = divider.remainder(new BigDecimal("2")).compareTo(BigDecimal.ZERO) == 0;
boolean isOdd = divider.remainder(new BigDecimal("2")).compareTo(BigDecimal.ZERO) != 0;
if (isDivisor && isEven) {
evenDivider++;
} else if (isDivisor && isOdd) {
oddDivider++;
}
}
}
if (oddDivider == evenDivider) { //found balanced number
System.out.println(potentialBalancedNumber);
break;
}
potentialBalancedNumber = potentialBalancedNumber.add(BigDecimal.ONE);
}
}
}
It seems to work fine but is too slow. Can you please help to find way to optimize it, am I missing something?
As #MarkDickinson suggested, answer is:
private static void findBalancedNumberOptimized(BigDecimal fromNumber) { //2,6,10,14,18,22,26...
if(fromNumber.compareTo(BigDecimal.ONE) == 0){
System.out.println(2);
}
else {
BigDecimal result = fromNumber.divide(new BigDecimal("4")).setScale(0, RoundingMode.HALF_UP).add(BigDecimal.ONE);
result = (TWO.multiply(result).subtract(BigDecimal.ONE)).multiply(TWO); //2(2n-1)
System.out.println(result);
}
}
and it's finally green, thanks Mark!

How to parse string into BigDecimal uniformly?

I am gettings strings as the input for a program, but those could be represented in various formats. E.g.:
8900
8.9E+3
89E+2
8900.000
All those numbers are equal mathematically and the following program also tells a match:
public class BigDecimalMain {
public static void main(String... args) {
BigDecimal a = new BigDecimal("8900");
BigDecimal b = new BigDecimal("8.9E+3");
BigDecimal c = new BigDecimal("89E+2");
BigDecimal d = new BigDecimal("8900.000");
System.out.println(a.compareTo(b));
System.out.println(a.compareTo(c));
System.out.println(a.compareTo(d));
}
}
Output:
0
0
0
My program uses .equals to match objects and it fails to give the right answer in the case above.
Question: How to parse string into BigDecimal uniformly? I mean the following:
find a function PARSE, that
for any STR1 && STR2
PARSE(STR1).compareTo(PARSE(STR2)) == 0 <=> PARSE(STR1).equals(PARSE(STR2))
With a common scale you could use setScale(xxx):
{
BigDecimal a = new BigDecimal("8900");
BigDecimal b = new BigDecimal("8.9E+3");
BigDecimal c = new BigDecimal("89E+2");
BigDecimal d = new BigDecimal("8900.000");
System.out.println(a.compareTo(b));
System.out.println(a.compareTo(c));
System.out.println(a.compareTo(d));
System.out.println(a.equals(b));
System.out.println(a.equals(c));
System.out.println(a.equals(d));
}
{
BigDecimal a = new BigDecimal("8900").setScale(5);
BigDecimal b = new BigDecimal("8.9E+3").setScale(5);
BigDecimal c = new BigDecimal("89E+2").setScale(5);
BigDecimal d = new BigDecimal("8900.000").setScale(5);
System.out.println(a.compareTo(b));
System.out.println(a.compareTo(c));
System.out.println(a.compareTo(d));
System.out.println(a.equals(b));
System.out.println(a.equals(c));
System.out.println(a.equals(d));
}
shows
0
0
0
false
false
false
0
0
0
true
true
true
Consider the following approach:
class UniformDecimal {
private final BigDecimal decimal;
public UniformDecimal(BigDecimal decimal) {
this.decimal = decimal;
}
#Override
public int hashCode() {
return decimal.toBigInteger().mod(BigInteger.valueOf((1<<31)-1)).intValue();
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
UniformDecimal that = (UniformDecimal) o;
return that.decimal.compareTo(this.decimal)==0;
}
}
Though, I'm not sure about hashCode implementation
UPDATE. It seems that stripTrailingZeros() does the thing!
Found something useful at here: HiveDecimal
private static BigDecimal normalize(BigDecimal bd, boolean allowRounding) {
if (bd == null) {
return null;
}
bd = trim(bd);
int intDigits = bd.precision() - bd.scale();
if (intDigits > MAX_PRECISION) {
return null;
}
int maxScale = Math.min(MAX_SCALE, Math.min(MAX_PRECISION - intDigits, bd.scale()));
if (bd.scale() > maxScale) {
if (allowRounding) {
bd = bd.setScale(maxScale, RoundingMode.HALF_UP);
// Trimming is again necessary, because rounding may introduce new trailing 0's.
bd = trim(bd);
} else {
bd = null;
}
}
return bd;
}
private static BigDecimal trim(BigDecimal d) {
if (d.compareTo(BigDecimal.ZERO) == 0) {
// Special case for 0, because java doesn't strip zeros correctly on that number.
d = BigDecimal.ZERO;
} else {
d = d.stripTrailingZeros();
if (d.scale() < 0) {
// no negative scale decimals
d = d.setScale(0);
}
}
return d;
}

Categories