I am using BigDecimal to get some price values. Requirement is something like this, what ever the value we fetch from database, the displayed valued should have 2 decimal points.
Eg:
fetched value is 1 - should be displayed as 1.00
fetched value is 1.7823 - should be displayed as 1.78
I am using setScale(2, BigDecimal.ROUND_HALF_UP) but still some places, if the data from DB is a whole number then the same is being displayed !!
I mean if the value is 0 from DB its displayed as 0 only. I want that to be displayed as 0.00
Thanks
BigDecimal is immutable, any operation on it including setScale(2, BigDecimal.ROUND_HALF_UP) produces a new BigDecimal. Correct code should be
BigDecimal bd = new BigDecimal(1);
bd.setScale(2, BigDecimal.ROUND_HALF_UP); // this does change bd
bd = bd.setScale(2, BigDecimal.ROUND_HALF_UP);
System.out.println(bd);
output
1.00
Note - Since Java 9 BigDecimal.ROUND_HALF_UP has been deprecated and you should now use RoundingMode.ROUND_HALF_UP.
you can use the round up format
BigDecimal bd = new BigDecimal(2.22222);
System.out.println(bd.setScale(2,BigDecimal.ROUND_UP));
Hope this help you.
To format numbers in JAVA you can use:
System.out.printf("%1$.2f", d);
where d is your variable or number
or
DecimalFormat f = new DecimalFormat("##.00"); // this will helps you to always keeps in two decimal places
System.out.println(f.format(d));
You need to use something like NumberFormat with appropriate locale to format
NumberFormat.getCurrencyInstance().format(bigDecimal);
BigDecimal.setScale would work.
The below code may help.
protected String getLocalizedBigDecimalValue(BigDecimal input, Locale locale) {
final NumberFormat numberFormat = NumberFormat.getNumberInstance(locale);
numberFormat.setGroupingUsed(true);
numberFormat.setMaximumFractionDigits(2);
numberFormat.setMinimumFractionDigits(2);
return numberFormat.format(input);
}
You can use a custom annotation in this manner:
The custom annotation
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface FormatBigDecimal {
String format() default "0.00";
}
And then you can apply the annotation on a field and in the constructor call the implementation method as shown below:
public class TestClass {
#FormatBigDecimal
private BigDecimal myDecimal;
public TestClass(BigDecimal myDecimal) throws ParseException, IllegalAccessException {
this.myDecimal = myDecimal;
formatBigDecimalFields();
}
public void setMyDecimal(BigDecimal myDecimal) {
this.myDecimal = myDecimal;
}
public BigDecimal getMyDecimal() {
return myDecimal;
}
/**
In the above method, we are using reflection to get all the fields declared in the class. Then, we are iterating over all the fields and checking whether the #FormatBigDecimal annotation is present on the field. If it is present, we are making the field accessible and getting its value.
We are also getting the format string from the #FormatBigDecimal annotation and using it to create a DecimalFormat object with the desired format. Then, we are formatting the value of the field using the DecimalFormat object and storing the formatted value in a string.
Finally, we are parsing the formatted value back into a BigDecimal object and setting it as the value of the field.
**/
public void formatBigDecimalFields() throws IllegalAccessException, ParseException {
Field[] fields = this.getClass().getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(FormatBigDecimal.class)) {
field.setAccessible(true);
BigDecimal value = (BigDecimal) field.get(this);
FormatBigDecimal formatAnnotation = field.getAnnotation(FormatBigDecimal.class);
String formatString = formatAnnotation.format();
NumberFormat format = NumberFormat.getNumberInstance(Locale.US);
DecimalFormat decimalFormat = (DecimalFormat) format;
decimalFormat.applyPattern(formatString);
String formattedValue = decimalFormat.format(value);
BigDecimal formattedDecimal = new BigDecimal(formattedValue);
field.set(this, formattedDecimal);
}
}
}
}
To Use it:
TestClass testClass = new TestClass(new BigDecimal("10000.899"));
System.out.println(testClass.getMyDecimal());
will give: 10000.90
Related
I want to parse LocalDateTime 2021-11-24T15:11:38.395 to LocalDateTime 2021-11-24T15:11:38.39. But LocalDateTime.parse() always adds zero in the end, ignoring my pattern.
public class DateTimeFormatUtils {
private static final String ISO_DATE_TIME = "yyyy-MM-dd'T'HH:mm:ss.SS";
public static LocalDateTime formatToISO(final LocalDateTime localDateTime) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(ISO_DATE_TIME);
return LocalDateTime.parse(formatter.format(localDateTime), formatter);
}
public static void main(String[] args) {
LocalDateTime now = LocalDateTime.now();
System.out.println(now);
System.out.println(formatToISO(now));
}
}
Output:
2021-11-30T11:48:28.449195200
2021-11-30T11:48:28.440
Is there way to deal with this problem?
Note that the strings "2021-11-24T15:11:38.39" and "2021-11-24T15:11:38.390" represent the same LocalDateTime. Technically, you've already got your expected output!
Since you say that the output is not what you expect, you are actually expecting a String as the output, since "2021-11-24T15:11:38.39" and "2021-11-24T15:11:38.390" are different strings. formatToISO should return a string - you should not parse the formatted date back to a LocalDateTime:
public static String formatToISO(final LocalDateTime localDateTime) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(ISO_DATE_TIME);
return formatter.format(localDateTime);
}
This is similar to the common mistake of beginners printing out doubles and expecting the specific format they have used to assign to the variable to come out.
double d = 5;
System.out.println(d); // expected 5, actual 5.0
LocalDateTime, just like double, doesn't store anything about how it should formatted. It just stores a value, and the same value will be formatted the same way.
Java fraction-of-seconds is always return 3 digits.
Work around is, first convert LocalDateTime to String, then remove last character of the string.
Off course, please validate null checks.
private static String removeLastDigit(String localDateTime) {
return localDateTime.substring(0, localDateTime.length()-1);
}
I've searched the Jackson docs, but can't find any good documentation for the pattern of #JsonFormat for floating point numbers.
Given a field
#JsonProperty("Cost")
private Double cost;
How can I get Jackson to format it as fixed point number with four digits precision in decimal format with #JsonFormat?
PS: I know one should not use floats for money. Spare us the discussion, please.
You would need to create a custom Serializer for that. Something like
#JsonProperty("amountOfMoney")
#JsonSerialize(using = MySerializer.class)
private Double cost;
public class MySerializerextends JsonSerializer<Double> {
#Override
public void serialize(Double value, JsonGenerator generator, SerializerProvider provider) throws IOException,
JsonProcessingException {
double roundedValue = value*10000;
roundedValue = Math.round(roundedValue );
roundedValue = roundedValue /10000;
generator.writeNumber(roundedValue );
}
}
You can see about the class here
https://fasterxml.github.io/jackson-databind/javadoc/2.3.0/com/fasterxml/jackson/databind/JsonSerializer.html
The rounding part might not be the best. You can do it as you prefer ;) Using decimal format can work too. If you use writeNumber it will print the value as a number in the result Json. That's why I changed my answer from writeString and using decimal format.
You should be able to use pattern of #JsonFormat for that if the implementation allows it.
Datatype-specific additional piece of configuration that may be used
to further refine formatting aspects. This may, for example, determine
low-level format String used for Date serialization; however, exact
use is determined by specific JsonSerializer
But with jackson I believe it works only for dates.
You can specify your own formatter in custom serializer class.
formatter = new DecimalFormat();
formatter.setMaximumFractionDigits(2);
formatter.setMinimumFractionDigits(2);
formatter.setGroupingUsed(false);
DecimalFormatSymbols sym = DecimalFormatSymbols.getInstance();
sym.setDecimalSeparator('.');
formatter.setDecimalFormatSymbols(sym);
Then, in actual serialize method:
final String output = formatter.format(value);
jsonGenerator.writeNumber(output);
Building on #Veselin's answers I'm using
public class DoubleDecimalSerializerWithSixDigitPrecisionAndDotSeparator
extends JsonSerializer<Double> {
#Override
public void serialize(Double value, JsonGenerator generator, SerializerProvider serializers)
throws IOException {
generator.writeNumber(String.format(Locale.US, "%.6f", value));
}
}
The use case is the generation of CSVs in Germany, so I don't care for JSON formatting and want a "." as a decimal separator.
How to validate a property that should be grater than zero?and not zero.I used built in annotation #Min(0) but how can i ignore the zero?Is there any other built in annotation for this case?
#Min(0)
default public Double getAmd1() {
return (Double) get("amd1");
}
Check out the #DecimalMin annotation
It provides a boolean parameter inclusive which fits your needs.
Sadly Double is not supported because of rounding issues, but BigDecimal is. You can then get the value as double by using getDouble().
So you can try something like this:
#DecimalMin(value = "0.0" ,inclusive = false)
private BigDecimal amd1;
public BigDecimal getAmd1() {
Double d = (Double) get("amd1");
return BigDecimal.valueOf(d);
}
I have a problem in retriving data with two decimal places from the database.
I was using resultset.getDouble() method. But this gives me only 1 decimal places on the result. I tried using the DecimalFormat to convert to 2 decimal. When I run my code, I'm getting the error "String cannot convert to double" from DisplayFood.java
DisplayFood.java
Connection connection = null;
Statement statement = null;
ResultSet resultset = null;
DecimalFormat df = new DecimalFormat("#.00");
String query = "SELECT * FROM food";
statement = connection.createStatement();
resultset = statement.executeQuery(query);
while(resultset.next())
{
FoodRecord foodmenu = new FoodRecord();
foodmenu.setItemName(resultset.getString("itemName"));
foodmenu.setPrice(df.format(resultset.getDouble("price")));
foodmenu.setRestaurant(resultset.getString("restaurant"));
food.add(foodmenu);
}
FoodRecord.java
public class FoodRecord {
private String itemName;
private double price;
private String restaurant;
public FoodRecord() {
}
public String getItemName() {
return itemName;
}
public void setItemName(String itemName) {
this.itemName = itemName;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public String getRestaurant() {
return restaurant;
}
public void setRestaurant(String restaurant) {
this.restaurant = restaurant;
}
}
You get this Error bacause You are trying to set a String value in a double Field.
Use resultset.getDouble("price") to read the items Price from resultset.
To print the Price with two decimal Places you can use your formatter with System.out.println like df.format(foodMenu.getPrice()).
I was using resultset.getDouble() method. But this gives me only 1
decimal places on the result
The getDouble() method returns the value number as a double is represented in java.
The getDouble() javadoc :
Retrieves the value of the designated column in the current row of
this ResultSet object as a double in the Java programming language.
It means if your number value is 10.5, the double will have as value 10.5 and not 10.50.
I tried using the DecimalFormat to convert to 2 decimal
DecimalFormat.format(double number) formats a double as a String with a specific format.
The format specified is here in your code :
DecimalFormat df = new DecimalFormat("#.00");
So, yes you cannot assign a Double to a String as these are not compatible types.
To solve your problem, you have to store the result of DecimalFormat.format(double number) in a String.
But it is indeed not not consistent with the FoodRecord class where price is a double field : private double price;
Changing price from double to String in the class is not necessarily the best way for two reasons :
the visual representation of a data should not change the data value.
If the decimal is on 4 floating value (for example : 10.5445), formatting it to 10.54 and storing the information in the class only as a String field truncates the data value. If you store again the value in your database, it changes the initial value while the object was not modified by the client.
you could not any longer have a simple setPrice() method with a double (so in a natural way) as you should perform some computations in the setPrice() method to change the double to a String.
To solve your problem, you could introduce a new method that returns the formatted price :
public String getFormatedPrice(){
return df.format(resultset.getDouble("price"));
}
If getFormatedPrice() is frequently invoked, the result could be cached instead of invoking df.format().
I am trying to read a config file and I want to use that properties value in some algebraic operations. So I need to convert the string returned by prop.getProperty(String str) into an integer.
I have tried converting it using:
1.)
Integer value = null;
String string = getProperty(key);
if (string != null)
value = new Integer(string);
return value;
2.)
String noofdivs = prop.getProperty("NO_OF_INITIAL_DIVS");
Integer noOfInitialDivs = Integer.valueOf(noofdivs);
3.)
String xyz = prop.getProperty("NO_OF_LINES_IN_A_DIV");
Integer noOfLinesInDiv = Integer.getInteger(xyz);
but none of them is working.
Can anybody help me out with this?
int value = Integer.parseInt(string);
You can then check for a NumberFormatException to see if it was properly parsed.
Even if it could be a little bit overengineered here, you could use Apache Commons Configuration to avoid converting yourself the properties. The framework has also the advantage to throw a ConversionException if the property your are reading is not of the expected type.
Integer value = env.getProperty("name property", Integer.class);