We are replacing some sybase code to Java and having issue with Rounding BigDecimal to match what sybase returns
11.4443999999999999062083588796667754650115966796875 - Should return 11.44
and
35.9549999999999982946974341757595539093017578125 - should return 35.96
I have tried all different rounding options with scale set to 2, but none works for both. What is the best option?
You'll have to take a two-step approach. First round to scale 3, then to scale 2, using RoundingMode.HALF_UP:
BigDecimal value1 = new BigDecimal("11.4443999999999999062083588796667754650115966796875");
BigDecimal value2 = new BigDecimal("35.9549999999999982946974341757595539093017578125");
BigDecimal rounded1 = value1.setScale(3, RoundingMode.HALF_UP); // 11.444
rounded1 = rounded1.setScale(2, RoundingMode.HALF_UP); // 11.44
BigDecimal rounded2 = value2.setScale(3, RoundingMode.HALF_UP); // 35.955
rounded2 = rounded2.setScale(2, RoundingMode.HALF_UP); // 35.96
This solution is not efficient but it gets the job done.
Source code
import java.math.BigDecimal;
public class BigDecimalRound {
public static void main(String[] args) {
BigDecimal bd;
float f;
bd = new BigDecimal(11.4443999999999999062083588796667754650115966796875);
f = roundBigDecimal(bd, 2);
System.out.println("bd: " + bd + ", f: " + f);
bd = new BigDecimal(35.9549999999999982946974341757595539093017578125);
f = roundBigDecimal(bd, 2);
System.out.println("bd: " + bd + ", f: " + f);
}
public static float roundBigDecimal(BigDecimal bd, int precision) {
int i = (int) Math.pow(10, precision);
return (float) Math.round(bd.floatValue() * i) / i;
}
}
Output
bd: 11.4443999999999999062083588796667754650115966796875, f: 11.44
bd: 35.9549999999999982946974341757595539093017578125, f: 35.96
Related
Here is the code:
public static void main(String[] args) {
final double d1 = 811.440000;
final double d2 = 425.530000;
final double d3 = 384.270000;
for (double d : Arrays.asList(d1, d2, d3)) {
final String dstr = String.format("%f", d);
BigDecimal bg1 = BigDecimal.valueOf(d).setScale(2, BigDecimal.ROUND_DOWN);
BigDecimal bg2 = (new BigDecimal(dstr)).setScale(2, BigDecimal.ROUND_DOWN);
BigDecimal bg3 = (new BigDecimal(d)).setScale(2, BigDecimal.ROUND_DOWN);
System.out.printf("[%s : %f] {%f, %f} %f\n", dstr, d, bg1, bg2, bg3);
}
}
Here is the output:
[811.440000 : 811.440000] {811.440000, 811.440000} 811.440000
[425.530000 : 425.530000] {425.530000, 425.530000} 425.520000
[384.270000 : 384.270000] {384.270000, 384.270000} 384.260000
Why don't we change valueOf(double) method or the BigDecimal(double) constructor of the BigDecimal class so as to get a consistent result?
The problem here isn't that one of new BigDecimal(double) nor new BigDecimal(String) working false.
The problem here is, that double are not precise. They store their bits in a way, which can't represent all numbers.
Here are some links about that topic:
Whats wrong with this simple 'double' calculation?
https://en.wikipedia.org/wiki/Floating-point_arithmetic#Accuracy_problems
Retain precision with double in Java
slightly change the code:
public static void main(String[] args) {
final double d1 = (new Double("811.44")).doubleValue();
final double d2 = (new Double("425.53")).doubleValue();
final double d3 = (new Double("384.27")).doubleValue();
final double DD = (new Double("999999999999999.95")).doubleValue(); // 15 9s before decimal point
for (double d : Arrays.asList(d1, d2, d3)) {
final String dstr = String.valueOf(d);
BigDecimal bg1 = BigDecimal.valueOf(d);
BigDecimal bg2 = (new BigDecimal(dstr));
BigDecimal bg3 = (new BigDecimal(d));
System.out.printf("* [%s : %s : %.15f] {%.15f, %.15f, %.15f}\n", dstr, d, d, bg1, bg2, bg3);
System.out.printf(" [%s : %s : %.15f] {%.15f, %.15f, %.15f}\n", dstr, d, d, bg1.doubleValue(), bg2.doubleValue(), bg3.doubleValue());
}
}
And this is the result:
1000000000000000.000000000000000
x [811.44 : 811.44 : 811.440000000000000] {811.440000000000000, 811.440000000000000, 811.440000000000055}
[811.44 : 811.44 : 811.440000000000000] {811.440000000000000, 811.440000000000000, 811.440000000000000}
x [425.53 : 425.53 : 425.530000000000000] {425.530000000000000, 425.530000000000000, 425.529999999999973}
[425.53 : 425.53 : 425.530000000000000] {425.530000000000000, 425.530000000000000, 425.530000000000000}
x [384.27 : 384.27 : 384.270000000000000] {384.270000000000000, 384.270000000000000, 384.269999999999982}
[384.27 : 384.27 : 384.270000000000000] {384.270000000000000, 384.270000000000000, 384.270000000000000}
From the output, it seems the lose of precision is introduced by the BigDecimal(double) constructor.
I'm having some issues with "i" in imaginary unit.
When I have "i" with number my program works. 4+4i it is ok.
But when I have only "i" does not want to work. 4+i doesn't work.
I have no idea how to change the code to solve this error. I know that below lines make this problem.
String x[] = str1.split("\\+|i|-");
String y[] = str2.split("\\+|i|-");
It is program for calculating multiplication(*), division(/), addition(+) and subtraction(-) of complex numbers typed as a string.
import java.text.DecimalFormat;
import java.util.Scanner;
public class Main {
private static DecimalFormat df = new DecimalFormat("0.0");
public static String Addition(double a_r, double a_i, double b_r, double b_i)
{
double x = a_r + b_r;
double y = a_i + b_i;
return df.format(x) + "+" + df.format(y) + "i";
}
public static String Subtraction(double a_r, double a_i, double b_r, double b_i)
{
double x = a_r - b_r;
double y = a_i - b_i;
return df.format(x) + "-" + df.format(y) + "i";
}
public static String Multiplication(double a_r, double a_i, double b_r, double b_i)
{
double x = a_r * b_r - a_i * b_i;
double y = a_r * b_i + a_i * b_r;
return df.format(x) + "+" + df.format(y) + "i";
}
public static String Division(double a_r, double a_i, double b_r, double b_i)
{
double x = a_r*b_r + a_i*b_i / b_r + b_i;
double y = a_r*b_i - a_i*b_r / b_r + b_i;
return df.format(x) + "+" + df.format(y) + "i";
}
public static void main(String[] args)
{
Scanner scan = new Scanner(System.in);
System.out.println("Num1");
String str1 = scan.nextLine();
System.out.println("Num2");
String str2 = scan.nextLine();
String x[] = str1.split("\\+|i|-");
String y[] = str2.split("\\+|i|-");
double a_real = Double.parseDouble(x[0]);
double a_img = Double.parseDouble(x[1]);
double b_real = Double.parseDouble(y[0]);
double b_img = Double.parseDouble(y[1]);
System.out.println(a_real);
System.out.println(a_img);
System.out.println(b_real);
System.out.println(b_img);
System.out.println(Addition(a_real, a_img, b_real, b_img));
System.out.println(Subtraction(a_real, a_img, b_real, b_img));
System.out.println(Multiplication(a_real, a_img, b_real, b_img));
System.out.println(Division(a_real, a_img, b_real, b_img));
}
}
Fix
You use the length of the array as condition
double a_img = x.length > 1 ? Double.parseDouble(x[1]) : 1;
double b_img = y.length > 1 ? Double.parseDouble(y[1]) : 1;
Improve
For now you code doesn't handle negative numbers, as the dash is in the split pattern. You may use a regex to match the parts you need : (-?\\d+)\\+?(-?\\d*)\\+?i
The parsing of the real part is easy, for the img you may check if the part is empty (case +i) and if the the part is just a dash (case -i)
Matcher m = Pattern.compile("(-?\\d+)\\+?(-?\\d*)\\+?i").matcher(value);
if (m.find()) {
System.out.println(m.group(1) + "<>" + m.group(2) + " ==>" + m.groupCount());
real = Double.parseDouble(m.group(1));
img = m.group(2).isEmpty() ? 1 : m.group(2).equals("-") ? -1 : Double.parseDouble(m.group(2));
} else {
throw new InvalidParameterException(value);
}
Object-Oriented programation
Designing a small class, the use could be like
class Complex {
private static final DecimalFormat df = new DecimalFormat("0.0");
private final double real, img;
public Complex(String value) {
Matcher m = Pattern.compile("(-?\\d+)\\+?(-?\\d*)\\+?i").matcher(value);
if (m.find()) {
System.out.println(m.group(1) + "<>" + m.group(2) + " ==>" + m.groupCount());
real = Double.parseDouble(m.group(1));
img = m.group(2).isEmpty() ? 1 : m.group(2).equals("-") ? -1 : Double.parseDouble(m.group(2));
} else {
throw new InvalidParameterException(value);
}
}
public Complex(double r, double i) {
real = r;
img = i;
}
public Complex add(Complex other) {
return new Complex(real + other.real, img + other.img);
}
public Complex substract(Complex other) {
return new Complex(real - other.real, img - other.img);
}
public Complex multiply(Complex other) {
return new Complex(real * other.real - img * other.img, real * other.img + img * other.real);
}
public Complex divide(Complex other) {
double denominator = Math.pow(other.real, 2) + Math.pow(other.img, 2);
return new Complex((real * other.real + img * other.img) / denominator,
(real * other.img - img * other.real) / denominator);
}
#Override
public String toString() { return df.format(real) + "+" + df.format(img) + "i";}
}
use
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
System.out.println("Input num1: ");
String str1 = scan.nextLine();
System.out.println("Input num2: ");
String str2 = scan.nextLine();
Complex c1 = new Complex(str1);
Complex c2 = new Complex(str2);
System.out.println(c1.add(c2));
System.out.println(c1.substract(c2));
System.out.println(c1.multiply(c2));
System.out.println(c1.divide(c2));
}
I have this method:
public void Example(BigDecimal value, int scale){
BigDecimal x = new BigDecimal("0.00001");
System.out.println("result: " + (value.multiply(x)).setScale(scale, RoudingMode.HALF_UP).toString());
If, per example, value = 1 and scale = 2, the output is "result: 0.00". I thought it would be 1.00E-5. So, my doubt is: How can I force a BigDecimal to be formated in scientific notation if its scale is bigger than a certain value (it was 2 in my example) ?
Here is a version of DannyMo's answer that sets the scale automatically:
private static String format(BigDecimal x)
{
NumberFormat formatter = new DecimalFormat("0.0E0");
formatter.setRoundingMode(RoundingMode.HALF_UP);
formatter.setMinimumFractionDigits((x.scale() > 0) ? x.precision() : x.scale());
return formatter.format(x);
}
System.out.println(format(new BigDecimal("0.01"))); // 1.0E-2
System.out.println(format(new BigDecimal("0.001"))); // 1.0E-3
System.out.println(format(new BigDecimal("500"))); // 5E2
System.out.println(format(new BigDecimal("500.05"))); // 5.00050E2
You can use a DecimalFormat with setMinimumFractionDigits(int scale):
private static String format(BigDecimal x, int scale) {
NumberFormat formatter = new DecimalFormat("0.0E0");
formatter.setRoundingMode(RoundingMode.HALF_UP);
formatter.setMinimumFractionDigits(scale);
return formatter.format(x);
}
...
System.out.println(format(new BigDecimal("0.00001"), 2)); // 1.00E-5
System.out.println(format(new BigDecimal("0.00001"), 3)); // 1.000E-5
You could use something like this:
int maxScale = 2;
BigDecimal value = BigDecimal.ONE;
BigDecimal x = new BigDecimal("0.00001");
BigDecimal result = value.multiply(x);
if (result.scale() > maxScale) {
System.out.format("result: %.2E\n", result); // You can change .2 to the value you need
} else {
System.out.println("result: " + result.toPlainString());
}
Try the DecimalFormat class. It has format methods for methods for double and long numbers so you should do something like this:
BigDecimal x = new BigDecimal("0.00001");
DecimalFormat frmt = new DecimalFormat("0.00E00");
String formatted = frmt.format(x.doubleValue());
System.out.println("result: " + formatted);
DecimalFormat javadoc
Alright im working on a radio program, and currently the radio frequencies are integers such as; 107900, 87900.
I need to convert numbers like that into strings that look like this,
107.9, 87.9
I've been playing around with DecimalFormat but haven't had any success. Any tips or hints are appreciated!
Here are some things i've tried,
frequency = 107900;
double newFreq = frequency / 1000;
String name = String.valueOf(newFreq);
result = 107.0
double freqer = 107900/1000;
DecimalFormat dec = new DecimalFormat("#.0");
result = 107.0
int frequency = 107900;
DecimalFormat dec = new DecimalFormat("#.0");
result = 107900.0
Thanks!
So as to not mess about with floating point, and assuming they're all one digit after the decimal (as radio stations are here, anyway), you can use:
String.format ("%d.%d", freq / 1000, (freq / 100) % 10)
See, for example, the following complete program:
public class Test {
static String radStat (int freq) {
return String.format ("%d.%d", freq / 1000, (freq / 100) % 10);
}
public static void main(String args[]) {
System.out.println("107900 -> " + radStat (107900));
System.out.println(" 87900 -> " + radStat ( 87900));
System.out.println("101700 -> " + radStat (101700));
}
}
which outputs:
107900 -> 107.9
87900 -> 87.9
101700 -> 101.7
I'm trying to figure a problem out with BigDecimal. My code:
BigDecimal tweetcount = new BigDecimal(3344048);
BigDecimal emotionCountBig = new BigDecimal(855937);
BigDecimal emotionCountSentenceBig = new BigDecimal(84988);
MathContext mc = new MathContext(64);
PMI[cnt] = (emotionCountSentenceBig.divide((tweetcount.multiply(emotionCountBig,mc)),RoundingMode.HALF_UP));
What I'd like to do is: emotionCountSentenceBig/(emotionCountBig*tweetcount)
(The values can be bigger)
If i try this I get a zero, which is not possible. Any help ?
You need to specify the MathContext for the division too:
emotionCountSentenceBig.divide(tweetcount.multiply(emotionCountBig, mc), mc);
That gives the expected result:
2.969226352632111794036880818610913852084810652372969382467557947E-8
Now as rightly commented by #PeterLawrey you could use doubles instead:
public static void main(String[] args) throws Exception {
double tweetcount = 3344048;
double emotionCount = 855937;
double emotionCountSentence = 84988;
double result = emotionCountSentence / (tweetcount * emotionCount);
System.out.println("result = " + result);
}
which prints:
result = 2.9692263526321117E-8
Note that if you use:
double result = 84988 / (3344048 * 855937);
you are actually doing your operations (* and /) on integer and it will return 0. You can prevent it by explicitly using a double, for example (note the d):
double result = 84988d / (3344048d * 855937);
I would use double
int tweetcount = 3344048;
int emotionCountBig = 855937;
int emotionCountSentenceBig = 84988;
double pmi = emotionCountSentenceBig/((double) tweetcount * emotionCountBig);
System.out.println(pmi);
prints
2.9692263526321117E-8
which is close to the answer using BigDecimal
2.969226352632111794036880818610913852084810652372969382467557947E-8