I have a JTable where i enter some values. This is the listener function to know if it has been made any change at the values, so i can update them. Values must have the format indicated (#.###), so I'm doing this fix to round the float to 3 decimal places if the client enter more than 3.
private class CambioTablaPretratamientoListener implements TableModelListener{
public void tableChanged(TableModelEvent e){
try{
if(enviarDatosADispositivo){
TableModel model = (TableModel)e.getSource();
float value = Float.parseFloat((String)model.getValueAt(e.getLastRow(), 1));
DecimalFormat dec = new DecimalFormat("#.###");
String aux = dec.format(value);
if(model.getValueAt(e.getLastRow(), 1).equals(aux))
return;
else
model.setValueAt(aux, e.getLastRow(), 1);
value = Float.parseFloat(aux);
String nombreAtributo = (String)model.getValueAt(e.getLastRow(), 0);
nodoAModificar.setPretratamientUserParameter(nombreAtributo, value);
}
}catch(BusinessException ex){
ex.printStackTrace();
}
}
}
Code seems to work at first, because it rounds and the number appears in the table, but the value is not getting updated. I guess I'm doing something wrong with the DecimalFormat, because if I leave the function like this one, it works:
private class CambioTablaPretratamientoListener implements TableModelListener{
public void tableChanged(TableModelEvent e){
try{
if(enviarDatosADispositivo){
TableModel model = (TableModel)e.getSource();
float value = Float.parseFloat((String)model.getValueAt(e.getLastRow(), 1));
String nombreAtributo = (String)model.getValueAt(e.getLastRow(), 0);
nodoAModificar.setPretratamientUserParameter(nombreAtributo, value);
}
}catch(BusinessException ex){
ex.printStackTrace();
}
}
}
And finall
You seem to think that converting a floating-point number to a String and back again can cause it to have a definite number of decimal places. This is fallacious. FP values don't have any decimal places. They have binary places. These only correspond to fixed numbers of decimal places if the fractional part is a negative power of 2, e.g. 0.5, 0.125, 0.0625, etc.
Instead, apply your DecimalFormat as a custom table cell renderer.
If you use BigDecimal, you could actually truncate the numbers, instead of trying to use Float's (lack of) precision.
Related
I have two values and I am trying to compare them, but getting the worng results:
public void subtotal() throws Exception {
WebDriverWait wait = new WebDriverWait(session.driver, 100);
double subtotal_price = 0;
DecimalFormat decimal = new DecimalFormat("0.00");
WebElement subtotal = wait.until(ExpectedConditions.visibilityOf( element("Subtotal_cart")));
Float subtotal_value = Float.parseFloat(subtotal.getText().substring(1));
logger.info("subtotal_value"+subtotal_value);
File file = new File("ItemUPC/ItemUPC.txt");
Scanner sc = new Scanner(file);
while (sc.hasNextLine()) {
String[] line = sc.nextLine().split("[|]");
String price = line[2];
subtotal_price = subtotal_price + Double.parseDouble(price);
}
logger.info("subtotal_price"+subtotal_price);
if ((subtotal_value)==(subtotal_price))
{
logger.info("Subtotals updated");
}
else
{
logger.info("Subtotals not updated");
}
}
The following is the ItemUPC file:
2|BATH BENCH|19.00
203|ORANGE BELL|1.78
When I print the value of subtotal_price and Subtotal_value I am getting both as 20.78, but when its getting compared in the if statement, I am getting output as "Subtotals not updated"
Not sure where I am getting wrong. Can someone please help? Thank you.
Comparing floating point numbers can be challenging, due to differences in precision between floating point types and their binary representations of decimal numbers.
You have two simple options:
Compare the absolute value of the difference between the two values to an epsilon, or threshold, value
Use BigDecimal as a substitute for your Float and double variable types
Example 1:
// simplification that may fail in certain edge cases
static final double EPSILON = .001; // acceptable error - adjust to suit your needs
if (Math.abs(subtotal_price - subtotal_value) < EPSILON) {
logger.info("Subtotals updated");
}
// ...
Example 2:
BigDecimal subtotal_price = new BigDecimal("0");
// ...
BigDecimal subtotal_value = new BigDecimal(subtotal.getText().substring(1));
// ...
if(subtotal_price.compareTo(subtotal_value) == 0) {
logger.info("Subtotals updated");
}
// ...
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);
I have a GUI program that simulates a fuel station.
In the program, there are 3 input fields:
itemName
number of Units(or volume in L)
and amount in pence (per unit or litre).
You can then chose to add the item by volume, or by units. The idea is that you can buy fuel and other items (such as food), with minimal input boxes.
I'm using exception handling to check the input is what I want it to be:
An int value to add by units
and a double value to add by volume.
My code so far recognises that a double has been entered where it wants an integer, and throws the error.
For example, the input of: item Name: Chocolate, Amount(or Litres): 2.5, Price: 85 gives the error: The code used looks like this
if (e.getSource () == AddByNumOfUnits) {
try {
Integer.parseInt(NumOfUnitsField.getText());
} catch (NumberFormatException exception) {
SetErrorField("Input must be the appropriate type (real number for volume, integer for units)");
}
However when adding by volume, I can't get the program to only accept double values, or anything that uses a decimal point. An int can be passed in and accepted as a double value, which I don't want. The code I'm using is very similar:
if (e.getSource() == AddByVolume) {
try {
double itemVolume = Double.parseDouble(NumOfUnitsField.getText());
} catch (NumberFormatException exception) {
SetErrorField("Input must be the appropriate type (real number for volume, integer for units)");
}
If anyone could point me in the right direction of any way of solving this, that would be great.
Thank you
Try this. It checks if the number contains a . char which would make it a double
try {
if(!NumOfUnitsField.getText().contains(".")){
throw new NumberFormatException("Not a double");
}
double itemVolume = Double.parseDouble(NumOfUnitsField.getText());
} catch (NumberFormatException exception) {
SetErrorField("Input must be the appropriate type (real number for volume, integer for units)");
}
EDIT: combined with codeboxs answer a solution would be
try {
Pattern p = Pattern.compile("\\d+\\.\\d+");
if(!p.matcher(NumOfUnitsField.getText()).matches()){
throw new NumberFormatException("Not a double");
}
double itemVolume = Double.parseDouble(NumOfUnitsField.getText());
} catch (NumberFormatException exception) {
SetErrorField("Input must be the appropriate type (real number for volume, integer for units)");
}
Double.parseDouble() will happily accept integer values, so you should probably try a regular expression instead. This checks that you have at least 1 digit before and after a decimal point:
Pattern p = Pattern.compile("\\d+\\.\\d+");
boolean isDecimalValue = p.matcher(NumOfUnitsField.getText()).matches();
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!
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;
}
}