implementing price of item using BigDecimal in java - java

In a web application ,I want to model an ItemForSale which has a price field.I read elsewhere that float or double should not be used for currency fields due to rounding errors and BigDecimal is the proper type for this purpose.I created these classes
class ItemForSale {
private String name;
private BigDecimal price;
...
}
class MyUtils{
...
public static BigDecimal parsePriceOfItem(String priceStr){
BigDecimal price;
BigDecimal zero = new BigDecimal(0);
try{
price = new BigDecimal(priceStr);
}catch(NumberFormatException nfe){
price = zero;
}
if(price.doubleValue() < zero.doubleValue()){
price = zero;
}
return price;
}
}
Is this the right way to parse a price string(as entered by user)?I wanted to treat negative and invalid strings (say 'abcd') as 0.
If there is a better way ,please tell me
thanks
mark

Why would you want to treat invalid input as 0? Surely you'd want to tell the user that they've made a mistake, rather than treating it as if they'd typed in zero.
If you're parsing user input, you should probably be using DecimalFormat instead of the BigDecimal constructor - that way it will use the appropriate cultural information. (Use setParseBigDecimal to make DecimalFormat parse to BigDecimal instead of double.)
Then instead of converting the BigDecimal values to doubles, use:
if (price.compareTo(BigDecimal.ZERO) < 0)
I would suggest that you should indicate to the user three different states:
Number can't be parsed
Number was negative (or possibly invalid in some other way; do you have a maximum value, or a maximum number of digits)?
Number was valid

How costly is your most expensive item? If it is less than $21,474,836.47 you can safely express the price as a number of cents held in a normal int.
You are correct to avoid float and double. The usual solution is to use an int, or a long, to hold the number of cents and adjust output formatting accordingly. There is usually no need to get into the complexities, and speed issues, of BigDecimal.

Here's my suggestion:
public static BigDecimal parsePriceOfItem(String priceStr) {
try {
BigDecimal price = new BigDecimal(priceStr);
return price.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : price;
} catch(NumberFormatException nfe) {
return BigDecimal.ZERO;
}
}

Related

getting wrong output while using numberFormat.parse("") method in java

I have below piece of code:
I am passing value "55.00000000000000" and getting output as 55.00000000000001.
But when i passed "45.00000000000000" and "65.00000000000000" i get output as 45.0 and 65.0.
Can someone please help me to get correct output as 55.0.
NumberFormat numberFormat = NumberFormat.getPercentInstance(Locale.US);
if (numberFormat instanceof DecimalFormat) {
DecimalFormat df = (DecimalFormat) numberFormat;
df.setNegativePrefix("(");
df.setNegativeSuffix("%)");
}
Number numericValue = numberFormat.parse("55.00000000000000%");
numericValue = new Double(numericValue.doubleValue() * 100);
System.out.println(numericValue);
The problem here is that numericValue is mathematically supposed to be 0.55. However, it will be a Double (because numberFormat.parse() can only return a Long or a Double). And a Double cannot hold the value 0.55 exactly. See this link for a complete explanation of why. The result is that as you do further computations with the inexact value, roundoff errors will occur, which is why the result being printed out is not quite the exact value. (A Double also cannot be exactly 0.45 or 0.65; it just happens that when multiplying by 100, the result rounds to the correct integer.)
When dealing with decimal values such as money or percentages, it's preferable to use BigDecimal. If the NumberFormat is a DecimalFormat, you can set things up so that parse returns a BigDecimal:
if (numberFormat instanceof DecimalFormat) {
DecimalFormat df = (DecimalFormat) numberFormat;
df.setNegativePrefix("(");
df.setNegativeSuffix("%)");
df.setParseBigDecimal(true); // ADD THIS LINE
}
Now, when you use numberFormat.parse(), the Number it returns will be a BigDecimal, which is able to hold the exact value 0.55. Now you have to avoid converting it to a double, which will introduce a roundoff error. Instead, you should say something like
Number numericValue = numberFormat.parse("55.00000000000000%");
if (numericValue instanceof BigDecimal) {
BigDecimal bdNumber = (BigDecimal) numericValue;
// use BigDecimal operations to multiply by 100, then print or format
// or whatever you want to do
} else {
// you're stuck doing things the old way, you might get some
// inaccuracy
numericValue = new Double(numericValue.doubleValue() * 100);
System.out.println(numericValue);
}
use this line of code
System.out.println(String.format("%.1f", numericValue));
Where format method use to format your data.

How to remove the E7 when a number is too high? [duplicate]

Here is my simple code
#Override
public void onClick(View v) {
try {
double price = Double.parseDouble(ePrice.getText().toString());
double percent = Double.parseDouble(ePercent.getText().toString());
double priceValue = price * percent/100.0f;
double percentValue = price - priceValue;
moneyToGet.setText(String.valueOf(priceValue));
moneyToPay.setText(String.valueOf(percentValue));
moneyToGet.setText("" + priceValue);
moneyToPay.setText("" + percentValue);
// catch
} catch (NumberFormatException ex) {
// write a message to users
moneyToGet.setText("");
}
}
});
This is a simple code for Percentage Calculator.
What I want is to avoid the Scientific Notation in my Calculator cause I don't want to explain to user what is Scientific Notation.
For example if I want to calculate 100,000,000 and cut 50% of it, it Should give me 50,000,000 which is giving me 5.0E7 And in my case this doesn't make any sense to the user. And of course I know both results are correct.
Thanks in Advance.
Check answer here. You can write
moneyToGet.setText(String.format("%.0f", priceValue));
You can try this DecimalFormat
DecimalFormat decimalFormatter = new DecimalFormat("############");
number.setText(decimalFormatter.format(Double.parseDouble(result)));
I would suggest using BigDecimals instead of doubles. That way you will have a more precise control over your calculation precision. Also you can get a non-scientific String using BigDecimal.toPlainString().
DecimalFormat decimalFormatter = new DecimalFormat("##.############");
decimalFormatter.setMinimumFractionDigits(2);
decimalFormatter.setMaximumFractionDigits(15);
This option will help you ##.## suffix 0 before decimal, otherwise output will be .000
btc.setText(decimalFormatter.format(btcval));
use this for displaying content
Use NumberFormater like
NumberFormat myformatter = new DecimalFormat("########");
String result = myformatter.format(yourValue);

Strange behaviour managing double and float values (Java)

I have a GUI which works like the following: there are 2 buttons and 1 textField. The textField is used to hold double/float values, 1 of the buttons adds a value (in this case, 0.1) and the other one subtracts (adds -0.1).
Here is my following problem: after pressing one of the buttons many times, the resulting value is not behaving the way I would like. In other words, instead of "1.5" turning into "1.6", it will be something like "1.5999998". I have tried many changes (like changing the variables types and the value to add/subtract), but none of these worked. Here's a piece of my code:
public void sumTextField(){
try{
if(textField.getText() == "")
textField.setText("0.1");
else{
float aux = Float.parseFloat(textField.getText());
aux += 0.10000000;
textField.setText(String.valueOf(aux));
}
}
catch(NumberFormatException nfe){
nfe.printStackTrace();
JOptionPane.showMessageDialog(null, "Please, provide a valid value in the text field!", "Impossible sum", JOptionPane.INFORMATION_MESSAGE);
}
}
public void subtractTextField(){
try{
if(textField.getText() == "")
textField.setText("-0.1");
else{
float aux = Float.parseFloat(textField.getText());
aux -= 0.10000000;
textField.setText(String.valueOf(aux));
}
}
catch(NumberFormatException nfe){
nfe.printStackTrace();
JOptionPane.showMessageDialog(null, "Please, provide a valid value in the text field!", "Impossible subtraction", JOptionPane.INFORMATION_MESSAGE);
}
}
Any ideas are welcome
Your problem is due to the way in which double and float work.
In floating-point arithmetic, the computer only calculates to a certain precision, i.e. after so many decimal places, it just rounds the number off. 0.1 may seem like a nice round number in decimal, but in binary it is recurring - 0.0001100110011 and so on. With each calculation, the rounding-off makes the result a bit more inaccurate. Have a look at this page for a more thorough explanation.
double is more precise than float, but it will still display rounding errors like this.
To circumvent the problem, you could do one of two things. First, as Jon Skeet said in the comments, you could use arbitrary-precision arithmetic like BigDecimal.
Alternatively, you could print out only a couple of decimal places like this:
String answer = String.format("%.2f", myNumber);
This will round off the printed value to 2 decimal places.
Hope this helps!

Refining an answer to x decimal places in java

I have an assignment and I need to get an input from the user to refine an answer to an x(the input of the user) number of decimal places. I'm going to refine my answer until there aren't any changes in the x decimal place.Can you please help on how I could achieve this answer?
It's not very clear what you are trying to achieve, but I think you want to accept a number and then round it up as the user specifies it.
Java's BigDecimal http://docs.oracle.com/javase/1.5.0/docs/api/java/math/BigDecimal.html class has all the functions you may need for this purpose. Please don't use the primary data types (float, double) as they will result in rounding errors sooner or later.
While it is true what #Thihara answers, maybe you need a bit simpler approach. Unless you need the precision of BigDecimal, you can do this:
int x = 4;
double value = 3.141593;
long answer = (long) (value * Math.pow(10, x));
The point is: multiply the value by 10^x and then convert to long (or int). Of course, this only works for small x.
There are a bunch of issues floating around here, that you should be aware of.
The first is that if you use a floating point number to represent your answer, you cannot represent every possible real number so you almost definitely will get rounding errors. Check out http://floating-point-gui.de/ for great information about this.
Secondly, when you print a float or double value, Java does some magic with it so that it looks nice. See Float.toString(float) and Double.toString(double) for more information.
So in reality, if you enter
double answer = 3.14159265;
it is stored as
3.141592650000000208621031561051495373249053955078125
which you can see using
System.out.println(new BigDecimal(answer));
So assuming you get your answer as a double (or float), you should use BigDecimal's setScale method. Also, if you want to limit the decimal places that your user can choose to the number visible when you print the double as a string, pass String.valueOf(answer) to BigDecimal's constructor.
Here is a little program that demonstrates how to do this
public static void main(String[] args) {
double answer = 3.14159265;
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String input = null;
do {
System.out.println("Answer: " + answer);
System.out.println("How many decimal places do you want? ");
try {
input = in.readLine();
} catch (IOException e) {
e.printStackTrace();
}
if (input != null) {
try {
int decimalPlaces = Integer.parseInt(input);
if (decimalPlaces < 0) {
System.out.println("Enter a positive value.");
} else {
BigDecimal scaled = new BigDecimal(
String.valueOf(answer));
if (decimalPlaces > scaled.scale()) {
System.out
.println("Answer does not have that many decimal places.");
} else {
scaled = scaled.setScale(decimalPlaces,
RoundingMode.HALF_EVEN);
System.out.println("Rounded answer: " + scaled);
}
}
} catch (Exception e) {
System.out.println("Not a valid number.");
}
}
} while (input != null);
}
Most of the code is error/input checking. The real work is done by setScale. Just keep in mind that there are many boundary conditions when working with floating point numbers, and you should be good!

Using BigDecimal to work with currencies

I was trying to make my own class for currencies using longs, but apparently I should use BigDecimal instead. Could someone help me get started? What would be the best way to use BigDecimals for dollar currencies, like making it at least but no more than 2 decimal places for the cents, etc. The API for BigDecimal is huge, and I don't know which methods to use. Also, BigDecimal has better precision, but isn't that all lost if it passes through a double? if I do new BigDecimal(24.99), how will it be different than using a double? Or should I use the constructor that uses a String instead?
Here are a few hints:
Use BigDecimal for computations if you need the precision that it offers (Money values often need this).
Use the NumberFormat class for display. This class will take care of localization issues for amounts in different currencies. However, it will take in only primitives; therefore, if you can accept the small change in accuracy due to transformation to a double, you could use this class.
When using the NumberFormat class, use the scale() method on the BigDecimal instance to set the precision and the rounding method.
PS: In case you were wondering, BigDecimal is always better than double, when you have to represent money values in Java.
PPS:
Creating BigDecimal instances
This is fairly simple since BigDecimal provides constructors to take in primitive values, and String objects. You could use those, preferably the one taking the String object. For example,
BigDecimal modelVal = new BigDecimal("24.455");
BigDecimal displayVal = modelVal.setScale(2, RoundingMode.HALF_EVEN);
Displaying BigDecimal instances
You could use the setMinimumFractionDigits and setMaximumFractionDigits method calls to restrict the amount of data being displayed.
NumberFormat usdCostFormat = NumberFormat.getCurrencyInstance(Locale.US);
usdCostFormat.setMinimumFractionDigits( 1 );
usdCostFormat.setMaximumFractionDigits( 2 );
System.out.println( usdCostFormat.format(displayVal.doubleValue()) );
I would recommend a little research on Money Pattern. Martin Fowler in his book Analysis pattern has covered this in more detail.
public class Money {
private static final Currency USD = Currency.getInstance("USD");
private static final RoundingMode DEFAULT_ROUNDING = RoundingMode.HALF_EVEN;
private final BigDecimal amount;
private final Currency currency;
public static Money dollars(BigDecimal amount) {
return new Money(amount, USD);
}
Money(BigDecimal amount, Currency currency) {
this(amount, currency, DEFAULT_ROUNDING);
}
Money(BigDecimal amount, Currency currency, RoundingMode rounding) {
this.currency = currency;
this.amount = amount.setScale(currency.getDefaultFractionDigits(), rounding);
}
public BigDecimal getAmount() {
return amount;
}
public Currency getCurrency() {
return currency;
}
#Override
public String toString() {
return getCurrency().getSymbol() + " " + getAmount();
}
public String toString(Locale locale) {
return getCurrency().getSymbol(locale) + " " + getAmount();
}
}
Coming to the usage:
You would represent all monies using Money object as opposed to BigDecimal. Representing money as big decimal will mean that you will have the to format the money every where you display it. Just imagine if the display standard changes. You will have to make the edits all over the place. Instead using the Money pattern you centralize the formatting of money to a single location.
Money price = Money.dollars(38.28);
System.out.println(price);
Or, wait for JSR-354. Java Money and Currency API coming soon!
1) If you are limited to the double precision, one reason to use BigDecimals is to realize operations with the BigDecimals created from the doubles.
2) The BigDecimal consists of an arbitrary precision integer unscaled value and a non-negative 32-bit integer scale, while the double wraps a value of the primitive type double in an object. An object of type Double contains a single field whose type is double
3) It should make no difference
You should have no difficulties with the $ and precision. One way to do it is using System.out.printf
Use BigDecimal.setScale(2, BigDecimal.ROUND_HALF_UP) when you want to round up to the 2 decimal points for cents. Be aware of rounding off error when you do calculations though. You need to be consistent when you will be doing the rounding of money value. Either do the rounding right at the end just once after all calculations are done, or apply rounding to each value before doing any calculations. Which one to use would depend on your business requirement, but generally, I think doing rounding right at the end seems to make a better sense to me.
Use a String when you construct BigDecimal for money value. If you use double, it will have a trailing floating point values at the end. This is due to computer architecture regarding how double/float values are represented in binary format.
Primitive numeric types are useful for storing single values in memory. But when dealing with calculation using double and float types, there is a problems with the rounding.It happens because memory representation doesn't map exactly to the value. For example, a double value is supposed to take 64 bits but Java doesn't use all 64 bits.It only stores what it thinks the important parts of the number. So you can arrive to the wrong values when you adding values together of the float or double type.
Please see a short clip https://youtu.be/EXxUSz9x7BM
I would be radical. No BigDecimal.
Here is a great article
https://lemnik.wordpress.com/2011/03/25/bigdecimal-and-your-money/
Ideas from here.
import java.math.BigDecimal;
public class Main {
public static void main(String[] args) {
testConstructors();
testEqualsAndCompare();
testArithmetic();
}
private static void testEqualsAndCompare() {
final BigDecimal zero = new BigDecimal("0.0");
final BigDecimal zerozero = new BigDecimal("0.00");
boolean zerosAreEqual = zero.equals(zerozero);
boolean zerosAreEqual2 = zerozero.equals(zero);
System.out.println("zerosAreEqual: " + zerosAreEqual + " " + zerosAreEqual2);
int zerosCompare = zero.compareTo(zerozero);
int zerosCompare2 = zerozero.compareTo(zero);
System.out.println("zerosCompare: " + zerosCompare + " " + zerosCompare2);
}
private static void testArithmetic() {
try {
BigDecimal value = new BigDecimal(1);
value = value.divide(new BigDecimal(3));
System.out.println(value);
} catch (ArithmeticException e) {
System.out.println("Failed to devide. " + e.getMessage());
}
}
private static void testConstructors() {
double doubleValue = 35.7;
BigDecimal fromDouble = new BigDecimal(doubleValue);
BigDecimal fromString = new BigDecimal("35.7");
boolean decimalsEqual = fromDouble.equals(fromString);
boolean decimalsEqual2 = fromString.equals(fromDouble);
System.out.println("From double: " + fromDouble);
System.out.println("decimalsEqual: " + decimalsEqual + " " + decimalsEqual2);
}
}
It prints
From double: 35.7000000000000028421709430404007434844970703125
decimalsEqual: false false
zerosAreEqual: false false
zerosCompare: 0 0
Failed to devide. Non-terminating decimal expansion; no exact representable decimal result.
How about storing BigDecimal into a database? Hell, it also stores as a double value??? At least, if I use mongoDb without any advanced configuration it will store BigDecimal.TEN as 1E1.
Possible solutions?
I came with one - use String to store BigDecimal in Java as a String into the database. You have validation, for example #NotNull, #Min(10), etc... Then you can use a trigger on update or save to check if current string is a number you need. There are no triggers for mongo though.
Is there a built-in way for Mongodb trigger function calls?
There is one drawback I am having fun around - BigDecimal as String in Swagger defenition
I need to generate swagger, so our front-end team understands that I pass them a number presented as a String. DateTime for example presented as a String.
There is another cool solution I read in the article above...
Use long to store precise numbers.
A standard long value can store the current value of the Unites States national debt (as cents, not dollars) 6477 times without any overflow. Whats more: it’s an integer type, not a floating point. This makes it easier and accurate to work with, and a guaranteed behavior.
Update
https://stackoverflow.com/a/27978223/4587961
Maybe in the future MongoDb will add support for BigDecimal.
https://jira.mongodb.org/browse/SERVER-1393
3.3.8 seems to have this done.
It is an example of the second approach. Use scaling.
http://www.technology-ebay.de/the-teams/mobile-de/blog/mapping-bigdecimals-with-morphia-for-mongodb.html
There is an extensive example of how to do this on javapractices.com. See in particular the Money class, which is meant to make monetary calculations simpler than using BigDecimal directly.
The design of this Money class is intended to make expressions more natural. For example:
if ( amount.lt(hundred) ) {
cost = amount.times(price);
}
The WEB4J tool has a similar class, called Decimal, which is a bit more polished than the Money class.
NumberFormat.getNumberInstance(java.util.Locale.US).format(num);

Categories