How to parse string into BigDecimal uniformly? - java

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;
}

Related

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.

Java - equals() and hashCode() implementation

I have a class PointDensity that is implemented as:
import java.sql.Date;
public class PointDensity {
private int id_place;
private String algorithm;
private Date mission_date;
private int mission_hour;
private int x;
private int y;
public PointDensity(int id_place, String algorithm, Date mission_date, int mission_hour, int x, int y) {
this.id_place = id_place;
this.algorithm = algorithm;
this.mission_date = mission_date;
this.mission_hour = mission_hour;
this.x = x;
this.y = y;
}
#Override
public boolean equals(Object obj){
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
PointDensity other = (PointDensity) obj;
return id_place == other.id_place
&& algorithm.equals(other.algorithm)
&& mission_date.equals(other.mission_date)
&& mission_hour == other.mission_hour
&& x == other.x
&& y== other.y;
}
#Override
public int hashCode(){
final int prime = 31;
int result = 1;
result = prime * result + y;
result = prime * result + ((algorithm == null) ? 0 : algorithm.hashCode());
result = prime * result + x;
result = prime * result + ((mission_date == null) ? 0 : mission_date.toString().hashCode());
result = prime * result + mission_hour;
result = prime * result + id_place;
return result;
}
}
I read some stuff and get values from a database. And then I want to use a HashMap() in order to store a PointDensity object as key and the times it has been encountered as a value. However, it never finds that the object is the same.
Map<PointDensity, Integer> pointDensities = new HashMap<>();
while (resultSet.next()){
PointDensity pointDensity =
new PointDensity(
resultSet.getInt(1),
resultSet.getString(2),
new Date(timestampValues.getLong(i)),
new java.util.Date(timestampValues.getLong(i)).getHours(),
xValues.getInt(i),
yValues.getInt(i)
);
if (pointDensities.containsKey(pointDensity)){
//IT NEVER ENTERS HERE!!!
System.out.println("exists");
int times = pointDensities.get(pointDensity);
pointDensities.replace(pointDensity, times++);
}else{
pointDensities.put(pointDensity, 1);
}
}
Thanks in advance :)
The following code works for me, your hashcode and equals methods seem OK. The error must be in code you aren't showing us, or in the database data itself.
public static void main( String[] args ) {
Map<PointDensity, Integer> pointDensities = new HashMap<>();
PointDensity pointDensity = new PointDensity( 10, "TEST",
new Date( 3, 1, 2019 ), new java.util.Date().getHours(),
42, 24 );
pointDensities.put( pointDensity, 1 );
System.out.println( "pointDensity equal to self:"
+ pointDensity.equals( pointDensity ) );
if( pointDensities.containsKey( pointDensity ) ) {
//IT NEVER ENTERS HERE!!!
System.out.println( "exists" );
int times = pointDensities.get( pointDensity );
pointDensities.replace( pointDensity, times++ );
} else {
pointDensities.put( pointDensity, 1 );
}
}
}
Output:
run:
pointDensity equal to self:true
exists
BUILD SUCCESSFUL (total time: 0 seconds)

stackoverflowerror when processing depth-first-iteration via scala

I intend to recursively iterate all grids within a circle zone, the code below will perform depth-first-search. But after 204 stacks, java.lang.StackOverflowError will be thrown.
def geohash_circle_around_point(lat: Double, lon: Double, radius: Double) = {
def expand_neighbors_impl(ghCenter: GeoHash, ghCur: GeoHash, buffer: collection.mutable.Set[GeoHash]): Unit = {
// MARK: DP: check whether it's iterated already or not
if(buffer contains ghCur) {
return
}
buffer += ghCur
for(ghAround <- get4GeoHashAround(ghCur)) {
if(distanceBetweenGeohash(ghCenter, ghAround) <= radius) {
expand_neighbors_impl(ghCenter, ghAround, buffer)
}
}
}
def get4GeoHashAround(gh: GeoHash): Array[GeoHash] = {
Array(gh.getNorthernNeighbour, gh.getSouthernNeighbour, gh.getWesternNeighbour, gh.getEasternNeighbour)
}
def distanceBetweenGeohash(gh1: GeoHash, gh2: GeoHash) = {
haversine(gh1.getBoundingBoxCenterPoint.getLatitude, gh1.getBoundingBoxCenterPoint.getLongitude, gh2.getBoundingBoxCenterPoint.getLatitude, gh2.getBoundingBoxCenterPoint.getLongitude)
}
val ghCenter = GeoHash.withBitPrecision(lat, lon, 40)
val s = collection.mutable.Set[GeoHash]()
expand_neighbors_impl(ghCenter, ghCenter, s)
s.map(_.getBoundingBox)
}
The stacktrace is as follows:
Exception in thread "main" java.lang.StackOverflowError
at scala.collection.mutable.HashSet.index(HashSet.scala:40)
at scala.collection.mutable.FlatHashTable$class.findElemImpl(FlatHashTable.scala:126)
at scala.collection.mutable.FlatHashTable$class.containsElem(FlatHashTable.scala:121)
at scala.collection.mutable.HashSet.containsElem(HashSet.scala:40)
at scala.collection.mutable.HashSet.contains(HashSet.scala:57)
at Test$.Test$$expand_neighbors_impl$1(Test.scala:32)
at Test$$anonfun$Test$$expand_neighbors_impl$1$1.apply(Test.scala:39)
at Test$$anonfun$Test$$expand_neighbors_impl$1$1.apply(Test.scala:37)
at scala.collection.IndexedSeqOptimized$class.foreach(IndexedSeqOptimized.scala:33)
at scala.collection.mutable.ArrayOps$ofRef.foreach(ArrayOps.scala:186)
at Test$.Test$$expand_neighbors_impl$1(Test.scala:37)
at Test$$anonfun$Test$$expand_neighbors_impl$1$1.apply(Test.scala:39)
at Test$$anonfun$Test$$expand_neighbors_impl$1$1.apply(Test.scala:37)
at scala.collection.IndexedSeqOptimized$class.foreach(IndexedSeqOptimized.scala:33)
at scala.collection.mutable.ArrayOps$ofRef.foreach(ArrayOps.scala:186)
at Test$.Test$$expand_neighbors_impl$1(Test.scala:37)
....
Could anyone give some suggestions? Thanks!
P.S.
Implementation for equals and hashCode for GeoHash:
public boolean equals(Object obj) {
if(obj == this) {
return true;
} else {
if(obj instanceof GeoHash) {
GeoHash other = (GeoHash)obj;
if(other.significantBits == this.significantBits && other.bits == this.bits) {
return true;
}
}
return false;
}
}
public int hashCode() {
byte f = 17;
int f1 = 31 * f + (int)(this.bits ^ this.bits >>> 32);
f1 = 31 * f1 + this.significantBits;
return f1;
}
Seems like you really need more than 200 calls at 40 precision...
You might want to consider rewriting your recursion to be tail-recursive, in order to be optimized by the compiler. Here's a way to do this:
#tailrec
def expand_neighbors_impl(ghCenter: GeoHash, toGoThrough: List[GeoHash], buffer: Set[GeoHash] = Set()): Set[GeoHash] = {
toGoThrough.headOption match {
case None => buffer
case Some(ghCur) =>
if (buffer contains ghCur) {
expand_neighbors_impl(ghCenter, toGoThrough.tail, buffer)
}
else {
val neighbors = get4GeoHashAround(ghCur).filter(distanceBetweenGeohash(ghCenter, _) <= radius)
expand_neighbors_impl(ghCenter, neighbors ++: toGoThrough, buffer + ghCur)
}
}
}
def expand_neighbors_impl(ghCenter: GeoHash, ghCur: GeoHash): Set[GeoHash] =
expand_neighbors_impl(ghCenter, List(ghCur))
Besides using tail-recursion, it avoids using a mutable Set, which might give some unexpected complication.

How to compare BigDecimal, but approximately?

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;
}

Rounding double or scientific notation

I want to round my number if it exceeds 8 character long.
For example,
// Big number rounding using scientific notation
double myDouble1 = 123456789;// desired output: 1.23e+08
Another situation
// Rounding
double myDouble2 = 12345.5678901234; // Desired output: 12345.57
I've tried using String.format() with %.2g and %.7, but I couldn't achieve the desired output.
Here's the code that I've tried to come up with.
public String parseResult(String val){
String formatted = val;
try{
if(formatted.length() > 8){
double temp = Double.parseDouble(val);
if(temp % 1 == 0){
formatted = String.format("%.2g", temp);
}else{
formatted = String.format("%.7g", temp);
}
}
}
catch(NumberFormatException e)
{
}
return formatted;
}
public class SolutionMain
{
public static void main(String[] args)
{
double myDouble1 = 123456789; // Desired output: 1.23e+08
double myDouble2 = 12345.5678901234; // Desired output: 12345.57
System.out.println(parseResult(myDouble1));
System.out.println(parseResult(myDouble2));
}
public static String parseResult(Double myDouble)
{
DecimalFormat format = null;
if(myDouble.toString().length() > 8)
{
if(myDouble % 1 == 0)
format = new DecimalFormat("0.00E00");
else
format = new DecimalFormat("#.00");
}
return format.format(myDouble);
}
}
For more pattern format details: Customizing Formats
class ScientificNot
{
public static String getScientifiNotation(double n)
{
int n1=(int)n;
String s0=String.valueOf(n-(double)n1);
String s1=String.valueOf((double)((int)n));
int in=s1.indexOf(".");
String mantissa=null,exp=null;
if(n>=10000000.0)
{
if(s1.length()>8)
{
mantissa=s1.substring(0,3);
exp=s1.substring(3);
double man=Double.parseDouble(mantissa)/100.0;
return(man+"e"+exp.length());
}
else
return s1;
}
else if(s0.length()>8)
{
double num=(((double)((int)(n*1000))));
int dp=((int)num%1000);
if(dp%10>=5)
dp=(dp-(dp%10))+10;
return String.valueOf(((int)num/1000)+"."+(dp/10));
}
else{
s1=""+n;
}
return s1;
}
}

Categories