Okay, here's my problem.
Basically I have this problem.
I have a number like .53999999.
How do I round it up to 54 without using any of the Math functions?
I'm guessing I have to multiply by 100 to scale it, then divide?
Something like that?
The issue is with money. let's say I have $50.5399999 I know how to get the $50, but I don't have how to get the cents. I can get the .539999 part, but I don't know how to get it to 54 cents.
I would use something like:
BigDecimal result = new BigDecimal("50.5399999").setScale(2, BigDecimal.ROUND_HALF_UP);
There is a great article called Make cents with BigDecimal on JavaWorld that you should take a look at.
You should use a decimal or currency type to represent money, not floating point.
Math with money is more complex than most engineers think (over generalization)
If you are doing currency calculations, I think you may be delving into problems that seem simple at their surface but are actually quite complex. For instance, rounding methods that are a result of business logic decisions that are repeated often can drastically affect the totals of calculations.
I would recommend looking at the Java Currency class for currency formatting.
Also having a look at this page on representing money in java may be helpful.
If this is homework, showing the teacher that you have thought through the real-world problem rather than just slung a bunch of code together that "works" - will surely be more impressive.
On a side note, I initially was going to suggest looking at the implementation of the Java math methods in the source code, so I took a look. I noticed that Java was using native methods for its rounding methods - just like it should.
However, a look at BigDecimal shows that there is Java source available for rounding in Java. So rather than just give you the code for your homework, I suggest that you look at the BigDecimal private method doRound(MathContext mc) in the Java source.
If 50.54 isn't representable in double precision, then rounding won't help.
If you're trying to convert 50.53999999 to a whole number of dollars and a whole number of cents, do the following:
double d = 50.539999; // or however many 9's, it doesn't matter
int dollars = (int)d;
double frac = d - dollars;
int cents = (int)((frac * 100) + 0.5);
Note that the addition of 0.5 in that last step is to round to the nearest whole number of cents. If you always want it to round up, change that to add 0.9999999 instead of 0.5.
Why would you not want to use any Math functions?
static long round(double a)
-Returns the closest long to the argument.
http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Math.html
To represent money I would take the following advice instead of re-inventing the wheel:
http://www.javapractices.com/topic/TopicAction.do?Id=13
Try storing your currency as number of cents (you could abstract this to number of base curreny units) with a long.
Edit: Since this is homework, you may not have control over the types. Consider this a lesson for future projects
long money = 5054;
long cents = money % 100;
long dollars = money / 100; // this works due to integer/long truncation
System.out.printf("$%d.%02.d", dollars, cents);
You need to make the number .535 and compare that with your original number to see if you'll round up or down. Here's how you get .535 from .53999999 (should work for any number):
num = .53999999;
int_num = (int)(num * 100); // cast to integer, however you do it in Java
compare_num = (int_num + 0.5) / 100;
compare_num would be .535 in this case. If num is greater than or equal to compare_num, round up to int_num + 1. Otherwise round down simply to int_num.
Sean seems to have it, except, if you want to impose proper rules then you may want to throw in an if statement like so:
double value = .539999;
int result = (int) (value*100);
if(((value*100)%result)>.5)
result++;
I suggest you use long for rounding a double value. It won't matter for small numbers but could make a difference.
double d = 50.539999;
long cents = (long)(d * 100 + 0.5);
double rounded = cents/100;
What exactly are you trying to do? Are you always trying to go to two digits? Or are you always trying to fix things like 99999 at the end no matter what?
According to the comments, it is in fact the former: rounding to dollars and cents. So indeed just round(100*a)/100 is what you want. (The latter would be much more complicated...)
Finally, if you just want to extract the cents part, the following would work:
dollars_and_cents = round(100*a)/100
cents = (dollars_and_cents-(int)dollars_and_cents)*100
(or does java just have frac? In which case the last line would just be frac(dollars_and_cents)*100.
Related
I have written the following simple function that calculates the arctan of the inverse of an integer. I was wondering how to use BigDecimal instead of double to increase the accuracy of the results. I was also thinking of using a BigInteger to store the growing multiples of xSquare that the "term" value is divided by.
I have limited experience with the syntax for how to perform calculations on BigDecimals. How would I revise this function to use them?
/* Thanks to https://www.cygnus-software.com/misc/pidigits.htm for explaining the general calculation method
credited to John Machin.
*/
public static double atanInvInt(int x) {
// Returns the arc tangent of an inverse integer
/* Terminates once the remaining amount reaches zero or the denominator reaches 2101.
If the former happens, the accuracy should be determined by the number format used, such as double.
If the latter happens, the result should be off by at most one from the correct nearest value
in the seventh decimal place, if allowed by the accuracy of the number format used.
This likely only happens if the integer is 1.
*/
int xSquare = x*x;
double result = ((double)1)/x;
double term = ((double)1)/x;
int divisor = 1;
double midResult;
while ((term > 0)) {
term = term / xSquare;
divisor += 2;
midResult = result - term/divisor;
term = term /xSquare;
divisor += 2;
result = midResult + term/divisor;
if (divisor >= 2101) {
return ((result + midResult) / 2);
}
}
return result;
}
The BigDecimal provides very intuitive wrapper methods to provide all the different operations. you can have something like this to have an arbitrary precision of, for example, 99:
public static void main(String[] args) {
System.out.println(atanInvInt(5, 99));
// 0.197395559849880758370049765194790293447585103787852101517688940241033969978243785732697828037288045
}
public static BigDecimal atanInvInt(int x, int scale) {
BigDecimal one = new BigDecimal("1");
BigDecimal two = new BigDecimal("2");
BigDecimal xVal = new BigDecimal(x);
BigDecimal xSquare = xVal.multiply(xVal);
BigDecimal divisor = new BigDecimal(1);
BigDecimal result = one.divide(xVal, scale, RoundingMode.FLOOR);
BigDecimal term = one.divide(xVal, scale, RoundingMode.FLOOR);
BigDecimal midResult;
while (term.compareTo(new BigDecimal(0)) > 0) {
term = term.divide(xSquare, scale, RoundingMode.FLOOR);
divisor = divisor.add(two);
midResult = result.subtract(term.divide(divisor, scale, RoundingMode.FLOOR));
term = term.divide(xSquare, scale, RoundingMode.FLOOR);
divisor = divisor.add(two);
result = midResult.add(term.divide(divisor, scale, RoundingMode.FLOOR));
if (divisor.compareTo(new BigDecimal(2101)) >= 0) {
return result.add(midResult).divide(two, scale, RoundingMode.FLOOR);
}
}
return result;
}
For anyone who wanted to know why it was beneficial to pose this question to begin with: That is a fair question. I have written a rather long answer to it. I believe that writing this answer helped me to articulate to myself things about the BigDecimal class that are more intuitive now that I have Armando Carballo’s answer than they were before, so writing it was hopefully educational. I can only hope that reading it will be as well, though likely in a different way if at all.
The official documentation lists methods, but it doesn’t explain how they are used in the same way that Armando Carballo’s code demonstrates. For example, while the way the BigDecimal.divide method works is pretty intuitive, there is nothing in the official documentation that says “to take the mean of two numbers, not only should you have BigDecimals for those two numbers, but you should also create a BigDecimal equal to 2 and apply the BigDecimal.divide method to the result of a BigDecimal.add operation with the 2 BigDecimal as the input for the divisor.” This is something that is simple enough to be perfectly intuitive once you see it, but if you’ve never used object-oriented methods for the specific purpose of performing arithmetic before, it may be less intuitive the first time you are trying to figure out how to take the mean.
As another example, consider the idea that to figure out whether a number is greater than or equal to another number, instead of using a Boolean operator on the two numbers, you use a compareTo method that can give three possible outputs on one number with the other number as an input, then apply a Boolean operator to the output of that method. This makes perfect sense once you see it in action and have a quick sense of how the compareTo method works, but may be less obvious when you’re staring at a quick description of the compareTo method in the official documentation, even if the description is clear and you are able to figure out what the compareTo method will output with a given BigDecimal value calling the method and a given BigDecimal input as the comparison value. For anyone who has used compareTo methods with other classes besides BigDecimal extensively, this is probably obvious even if they’re new to the specific class, but if you haven’t used Booleans on the result of ANY compareTo method recently, it’s faster to see it.
When working with ints, you might very well write code a bit like this:
int x = 5;
x = x + 2;
System.out.println(x) // should be 7
Here, the “2” value was never declared to be an int. The result of the addition was the same as if we had declared y=2 and said that x = x+y instead of x = x+2, but with the above lines of code no named variable, or Integer object if we used those instead of primitive ints, was created for the “2”. With BigDecimal, on the other hand, since the BigDecimal.add method requires BigDecimals as inputs, it would be mandatory to create a BigDecimal equal to 2 in order to add 2. I don’t see anything in the official documentation that says “use this as a more accurate substitute for doubles, or for longs if you want something more versatile than BigInteger, but in addition to using it as a substitute for declared variables, also create BigDecimal objects equal to small integers that by themselves wouldn’t call for the use of the BigDecimal class so that you can use them in operations. Both your variables and the small values you are adding to them need to be BigDecimals if you want to use BigDecimals.”
Finally, let me explain something that has the potential to make the BigDecimal class more intimidating than it needs to be. Anyone who has ever worked with primitive arrays and tried to predict in advance at the time the array is created exactly how large it needs to be, or is familiar with how lower-level languages involve certain situations in which a programmer needs to know exactly how many bytes something takes up, may feel the need for caution when dealing with something that seems to demand a specified level of precision upfront. The documentation says this: “If no rounding mode is specified and the exact result cannot be represented, an exception is thrown; otherwise, calculations can be carried out to a chosen precision and rounding mode by supplying an appropriate MathContext object to the operation.” A newbie reading that sentence for the first time may be thinking that they are going to have to think extensively about rounding when writing their code for the first time or else face exceptions as soon as a value cannot be represented exactly, or that they are going to have to read the documentation on the MathContext object as well before using BigDecimal, which in turn might lead to reading IEEE standards that help grant an understanding of floating point numbers but are far removed from what the person actually wanted to code. Seeing that some of the constructors for BigDecimal take arrays as inputs and that others take a MathContext as an input, along with noticing that one of the constructors for the related BigInteger class takes a byte array as the input, may strengthen the feeling that using this object class requires a very fine understanding of the exact number of digits that will be used for the specific calculations the class is used for and that understanding MathContext is more or less essential to even the most basic use of the class. While I’m sure understanding MathContext is helpful, baby’s first BigDecimal project can actually work perfectly well without the need to learn this added functionality at the same time as the first use of the BigDecimal. Reading up on the scale parameter might also lead to the belief by a coder looking up info on the class for the first time that it is necessary to predict the order of magnitude of the answer in advance in order to use the class at all.
Armando Caballo’s commendable answer shows that these concerns of a hypothetical newbie are overblown, as while rounding mode does need to be specified fairly often and a consistent scale is often called as a parameter when using the divide method, the scale parameter is actually a fairly arbitrary specification of the desired accuracy in terms of number of decimal places and not something that requires pinpoint predictions about exactly what numbers the class will handle (unless the ultimate purpose for which the BigDecimal is being used requires a finely controlled level of accuracy, in which case it is fairly easy to specify). An “infinite” series of added and subtracted terms to compute an arc tangent was processed without ever declaring a MathContext object.
I am trying to figure out what is the best way to have two decimal scaling for prices.
So here is the scenario. Lets say I have full price of 100.00 and after discount you pay 90. So the discount percent is 10%. To achieve this I wrote something like, which works fine
BigDecimal grossPrice = new BigDecimal(100);
BigDecimal discountedPrice = new BigDecimal(90);
BigDecimal.ONE.subtract(discountedPrice.divide(grossPrice,2, RoundingMode.HALF_EVEN))
.multiply(BigDecimal.valueOf(100))
.setScale(2, RoundingMode.HALF_EVEN)
.doubleValue();
But as soon as I change discountedPrice to 89.5, and I expect discount percent to 10.5, but I still get 10 and the reason is clear because 89.5/100 gives 0.895 and since its half even rounds it to 0.9 and so still 10%
If i do HALF_UP, its as good as half_even. If i do, HALF_DOWN, the value will be 0.89 and I will have discount percent as 11. So I am bit confuse as to what will actually give me 10.5% discount in this case.
How about setting scale to 3. Remember to change it to both places:
At where you divide,
At where you round to half even.
BigDecimal grossPrice = new BigDecimal(100);
BigDecimal discountedPrice = new BigDecimal("89.5");
double doubleValue = BigDecimal.ONE
.subtract(discountedPrice.divide(grossPrice,3, RoundingMode.HALF_EVEN)) // here
.multiply(BigDecimal.valueOf(100))
.setScale(3, RoundingMode.HALF_EVEN) // here
.doubleValue();
System.out.println(doubleValue); // 10.5
You might want to define MathContext to support your calculations and avoid typos:
MathContext halfEvenTo3 = new MathContext(3, RoundingMode.HALF_EVEN);
BigDecimal grossPrice = new BigDecimal(100);
BigDecimal discountedPrice = new BigDecimal("89.5");
double doubleValue = BigDecimal.ONE
.subtract(discountedPrice.divide(grossPrice, halfEvenTo3)) // here
.multiply(BigDecimal.valueOf(100))
.round(halfEvenTo3) // here
.doubleValue();
System.out.println(doubleValue); // 10.5
Given your rounding mode, it seems clear that you intend for the system to never end up at fractions of cents. A good plan; most financial systems cannot deal with fractional cents (you can't transfer half of a cent, or pay half a cent at the register, or even enter half a cent in a POS tool).
That means you can ditch BigDecimal entirely, and use plain jane long, which will then represent the number of 'atomic currency units'. That'd be yen for yen, cents for euros and dollars, pennies for pounds, satoshis for bitcoin, etcetera.
The numbers you are trying to represent here are completely different, and it's good to go back to what things mean, first, and then write code.
In your code, grossPrice and discountedPrice are both monetary amounts.
On the other hand, the value you are looking for isn't a monetary amount at all: It's a completely different thing - a ratio. This also shows in what you desire: For the monetary amounts, you wish to round to 2 decimals at all times, but for the ratio you don't, which is sensible - they are 2 completely different concepts.
Ratios are tricky. For example, the ratio between 1 and 3 is not perfectly representable in either base 10 (0.333333) or the base2 that e.g. double and co use. Therefore, it is not possible as a general rule to have perfect ratios. You must therefore kiss that goodbye and pick, instead, some arbitrary precision.
One easy way out is to just say: Well, hey, if perfection is no longer on the table, I'll make sure my code and documentation is written to keep that in mind, and I don't need it anymore. In which case, you might as well go with double. double is a horrible idea to represent monetary amounts with, but ratios - that's fine.. and in your code you go to double anyway.
In other words, stop using BigDecimal here: You are using it for 2 things, and in both cases, there wasn't an actual point:
You use it to represent monetary amounts, but in a way that fractional atomic units are impossible. You can do that, but it's overengineered and needlessly complex, just use a long to store those atomic units instead.
You use it to calculate a ratio, but you convert it to a double, so whatever fine-grained precision control you wanted to apply is undone by your conversion.
long grossPrice = 10000; // $100
long discountedPrice = 8950; // $89.50
double ratio = 100.0 * discountedPrice / grossPrice;
When printing these numbers, that's where you bring the decimals in:
System.out.printf("Ratio: %.3f\n", ratio); // print to 3 decimal places.
If you do need the fine grained control, or you want fractional atomic finance units, then please keep in mind:
In many ways 'fractional atomic finance' is unsolvable. For example, if you want to split a 4-cent surcharge across 3 people, you just can't do that, and no amount of BigDecimal is going to help you here: BigDecimal is incapable of perfectly representing '1 1/3 of a cent', and even if somehow it could, you can't actually charge that. The best solution would be to either round up (charge each member 2 cents), or to flip a coin, and charge 2 people 1 cent, and randomly charge one person 2 cents. There is no way to do this automatically, you'd have to code it up. In general, once division or ratios are involved, perfection is off the table.
There are libraries for currencies, such as joda-currency. You may want to use those instead, if your aim is 'convenience' (BigDecimal isn't very convenient, hence why 'I'll use BD instead of longs; more convenient' is a weird conclsuion).
longs CAN overflow/underflow, but that isn't relevant unless you're talking about centuries's worth of world economic output. You need a lot of money to overflow the model of 'store cents in a long'.
int however, that's no good: You can easily overflow those (40 million bucks is all you need!), so make sure to use longs.
I've been trying to refactor a real time application that uses BigDecimal, to a fixed point representation with 10 decimal digits using a long, as it's what is commonly advised to store and manipulate money in Java. The problem is I need to deal with ratios as well (exchange rates, discount rates, percentages, etc) and I'm not sure how to do that in a sane way.
So to create those decimals I do this (encapsulated in a class of course):
long multiplier = (long) Math.pow(10, precision)
long decimal = 1.2345 * multiplier; // 12345000000
Here are some basic operations with this representation:
long addition = decimal1 + decimal2;
long subtraction = decimal1 - decimal2;
long multiplication = (decimal1 * decimal2) / multiplier;
long division = (decimal1 * multiplier) / decimal2;
As you can see, multiplications and divisions written like that are prone to overflows even for small numbers. That reduces the range of valid arguments for these operations greatly.
So some may ask why do you need that in the first place? Well, to convert from a currency to another for instance: eur=usd*rate, or to get the return of an investment: roi=profit/investment.
What is the real way to go to get good accuracy up to a certain number of digits and excellent performance (as close as possible to using primitive types)?
Use primitive types and store cents (not dollars/euros/whatever)!
That’s what banks do, and what you should do too.
Putting a decimal point in a number is a rendering issue; don’t let rendering influence your core data type.
A long of cents will accurately hold the total global debt thousands of times over - it’s big enough to hold any value you will ever encounter.
Your question doesn't make sense.
You only multiply money by scalars. It is meaningless to multiply two money amounts together. What exactly are you expecting? Square money?
The result of dividing a money amount by another is not a money amount. It is a ratio, e.g. an exchange rate.
NB a long holds a lot more than 10 decimal digits. More like 20 offhand.
I'm wondering what the best way to fix precision errors is in Java. As you can see in the following example, there are precision errors:
class FloatTest
{
public static void main(String[] args)
{
Float number1 = 1.89f;
for(int i = 11; i < 800; i*=2)
{
System.out.println("loop value: " + i);
System.out.println(i*number1);
System.out.println("");
}
}
}
The result displayed is:
loop value: 11
20.789999
loop value: 22
41.579998
loop value: 44
83.159996
loop value: 88
166.31999
loop value: 176
332.63998
loop value: 352
665.27997
loop value: 704
1330.5599
Also, if someone can explain why it only does it starting at 11 and doubling the value every time. I think all other values (or many of them at least) displayed the correct result.
Problems like this have caused me headache in the past and I usually use number formatters or put them into a String.
Edit: As people have mentioned, I could use a double, but after trying it, it seems that 1.89 as a double times 792 still outputs an error (the output is 1496.8799999999999).
I guess I'll try the other solutions such as BigDecimal
If you really care about precision, you should use BigDecimal
https://docs.oracle.com/javase/8/docs/api/java/math/BigDecimal.html
https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/math/BigDecimal.html
The problem is not with Java but with the good standard float's (http://en.wikipedia.org/wiki/IEEE_floating-point_standard).
You can either:
use Double and have a bit more precision (but not perfect of course, it also has limited precision)
use a arbitrary-precision-library
use numerically stable algorithms and truncate/round digits of which you are not sure they are correct (you can calculate numeric precision of operations)
When you print the result of a double operation you need to use appropriate rounding.
System.out.printf("%.2f%n", 1.89 * 792);
prints
1496.88
If you want to round the result to a precision, you can use rounding.
double d = 1.89 * 792;
d = Math.round(d * 100) / 100.0;
System.out.println(d);
prints
1496.88
However if you see below, this prints as expected, as there is a small amount of implied rounding.
It worth nothing that (double) 1.89 is not exactly 1.89 It is a close approximation.
new BigDecimal(double) converts the exact value of double without any implied rounding. It can be useful in finding the exact value of a double.
System.out.println(new BigDecimal(1.89));
System.out.println(new BigDecimal(1496.88));
prints
1.8899999999999999023003738329862244427204132080078125
1496.8800000000001091393642127513885498046875
Most of your question has been pretty well covered, though you might still benefit from reading the [floating-point] tag wiki to understand why the other answers work.
However, nobody has addressed "why it only does it starting at 11 and doubling the value every time," so here's the answer to that:
for(int i = 11; i < 800; i*=2)
╚═══╤════╝ ╚╤═╝
│ └───── "double the value every time"
│
└───── "start at 11"
You could use doubles instead of floats
If you really need arbitrary precision, use BigDecimal.
first of Float is the wrapper class for the primitive float
and doubles have more precision
but if you only want to calculate down to the second digit (for monetary purposes for example) use an integer (as if you are using cents as unit) and add some scaling logic when you are multiplying/dividing
or if you need arbitrary precision use BigDecimal
If precision is vital, you should use BigDecimal to make sure that the required precision remains. When you instantiate the calculation, remember to use strings to instantiate the values instead of doubles.
I never had a problem with simple arithmetic precision in either Basic, Visual Basic, FORTRAN, ALGOL or other "primitive" languages. It is beyond comprehension that JAVA can't do simple arithmetic without introducing errors. I need just two digits to the right of the decimal point for doing some accounting. Using Float subtracting 1000 from 1355.65 I get 355.650002! In order to get around this ridiculous error I have implemented a simple solution. I process my input by separating the values on each side of the decimal point as character, convert each to integers, multiply each by 1000 and add the two back together as integers. Ridiculous but there are no errors introduced by the poor JAVA algorithms.
I've inherited a project in which monetary amounts use the double type.
Worse, the framework it uses, and the framework's own classes, use double for money.
The framework ORM also handles retrieval of values from (and storage to) the database. In the database money values are type number(19, 7), but the framework ORM maps them to doubles.
Short of entirely bypassing the framework classes and ORM, is there anything I can do to calculate monetary values precisely?
Edit: yeah, I know BigDecimal should be used. The problem is that I am tightly tied to a framework that where, e.g., the class framework.commerce.pricing.ItemPriceInfo has members double mRawTotalPrice; and double mListPrice. My company's application's own code extends, e.g, this ItemPriceInfoClass.
Realistically, I can't say to my company, "scrap two years of work, and hundreds of thousands of dollars spent, basing code on this framework, because of rounding errors"
If tolerable, treat the monetary type as integral. In other words, if you're working in the US, track cents instead of dollars, if cents provides the granularity you need. Doubles can accurately represent integers up to a very large value (2^53) (no rounding errors up to that value).
But really, the right thing to do is bypass the framework entirely and use something more reasonable. That's such an amateur mistake for the framework to make - who knows what else is lurking?
I didn't see you mention refactoring. I think that's your best option here. Instead of throwing together some hacks to get things working better for now, why not fix it the right way?
Here's some information on double vs BigDecimal. This post suggests using BigDecimal even though it is slower.
Plenty of people will suggest using BigDecimal and if you don't know how to use rounding in your project, that is what you should do.
If you know how to use decimal rounding correctly, use double. Its many orders of magnitude faster, much clear and simpler and thus less error prone IMHO. If you use dollars and cents (or need two decimal places), you can get an accurate result for values up to 70 trillion dollars.
Basically, you won't get round errors if you correct for it using approriate rounding.
BTW: The thought of rounding errors strikes terror into the heart of many developers, but they are not random errors and you can manage them fairly easily.
EDIT: consider this simple example of a rounding error.
double a = 100000000.01;
double b = 100000000.09;
System.out.println(a+b); // prints 2.0000000010000002E8
There are a number of possible rounding strategies. You can either round the result when printing/displaying. e.g.
System.out.printf("%.2f%n", a+b); // prints 200000000.10
or round the result mathematically
double c = a + b;
double r= (double)((long)(c * 100 + 0.5))/100;
System.out.println(r); // prints 2.000000001E8
In my case, I round the result when sending from the server (writing to a socket and a file), but use my own routine to avoid any object creation.
A more general round function is as follows, but if you can use printf or DecimalFormat, can be simpler.
private static long TENS[] = new long[19]; static {
TENS[0] = 1;
for (int i = 1; i < TENS.length; i++) TENS[i] = 10 * TENS[i - 1];
}
public static double round(double v, int precision) {
assert precision >= 0 && precision < TENS.length;
double unscaled = v * TENS[precision];
assert unscaled > Long.MIN_VALUE && unscaled < Long.MAX_VALUE;
long unscaledLong = (long) (unscaled + (v < 0 ? -0.5 : 0.5));
return (double) unscaledLong / TENS[precision];
}
note: you could use BigDecimal to perform the final rounding. esp if you need a specifc round method.
Well, you don't have that many options in reality:
You can refactor the project to use e.g. BigDecimal (or something that better suits its needs) to represent money.
Be extremely careful for overflow/underflow and loss of precision, which means adding tons of checks, and refactoring even larger proportion of the system in an unnecessary way. Not to mention how much research would be necessary if you are to do that.
Keep things the way they are and hope nobody notices (this is a joke).
IMHO, the best solution would be to simply refactor this out. It might be some heavy refactoring, but the evil is already done and I believe that it should be your best option.
Best,
Vassil
P.S. Oh and you can treat money as integers (counting cents), but that doesn't sound like a good idea if you are going to have currency conversions, calculating interest, etc.
I think this situation is at least minimally salvageable for your code. You get the value as a double via the ORM framework. You can then convert it to BigDecimal using the static valueOf method (see here for why) before doing any math/calculations on it, and then convert it back to double only for storing it.
Since you are extending these classes anyway, you can add getters for your double value that gets them as BigDecimal when you need it.
This may not cover 100% of the cases (I would be especially worried about what the ORM or JDBC driver is doing to convert the double back to a Number type), but it is so much better than just doing the math on the raw doubles.
However, I am far from convinced that this approach is actually cheaper for the company in the long run.