I'm using Java's DecimalFormat class to print out numbers in Scientific Notation. However, there is one problem that I have. I need the strings to be of fixed length regardless of the value, and the sign on the power of ten is throwing it off. Currently, this is what my format looks like:
DecimalFormat format = new DecimalFormat("0.0E0");
This gives me the following combinations: 1.0E1, 1.0E-1, -1.0E1, and -1.0E-1.
I can use setPositivePrefix to get: +1.0E1, +1.0E-1, -1.0E1, and -1.0E-1, or whatever I like, but it doesn't affect the sign of the power!
Is there any way to do this so that I can have fixed length strings? Thanks!
Edit: Ah, so there's no way to do it using Java's existing DecimalFormat API? Thanks for the suggestions! I think I may have to subclass DecimalFormat because I am limited by the interface that is already in place.
This worked form me,
DecimalFormatSymbols SYMBOLS = DecimalFormatSymbols.getInstance(Locale.US);
if (value > 1 || value < -1) {
SYMBOLS.setExponentSeparator("e+");
} else {
SYMBOLS.setExponentSeparator("e");
}
DecimalFormat format = new DecimalFormat(sb.toString(), SYMBOLS);
Here's one way. Hokey, perhaps, but it works...
public class DecimalFormatTest extends TestCase {
private static class MyFormat extends NumberFormat {
private final DecimalFormat decimal;
public MyFormat(String pattern) {
decimal = new DecimalFormat(pattern);
}
public StringBuffer format(double number, StringBuffer toAppendTo, FieldPosition pos) {
StringBuffer sb = new StringBuffer();
sb.append(modified(Math.abs(number) > 1.0, decimal.format(number, toAppendTo, pos).toString()));
return sb;
}
private String modified(boolean large, String s) {
return large ? s.replace("E", "E+") : s;
}
public StringBuffer format(long number, StringBuffer toAppendTo, FieldPosition pos) {
StringBuffer sb = new StringBuffer();
sb.append(modified(true, decimal.format(number, toAppendTo, pos).toString()));
return sb;
}
public Number parse(String source, ParsePosition parsePosition) {
return decimal.parse(source, parsePosition);
}
public void setPositivePrefix(String newValue) {
decimal.setPositivePrefix(newValue);
}
}
private MyFormat format;
protected void setUp() throws Exception {
format = new MyFormat("0.0E0");
format.setPositivePrefix("+");
}
public void testPositiveLargeNumber() throws Exception {
assertEquals("+1.0E+2", format.format(100.0));
}
public void testPositiveSmallNumber() throws Exception {
assertEquals("+1.0E-2", format.format(0.01));
}
public void testNegativeLargeNumber() throws Exception {
assertEquals("-1.0E+2", format.format(-100.0));
}
public void testNegativeSmallNumber() throws Exception {
assertEquals("-1.0E-2", format.format(-0.01));
}
}
Alternatively you could subclass DecimalFormat, but I find it generally cleaner not to subclass from concrete classes.
Could you use printf() instead:
Format format = new DecimalFormat("0.0E0");
Double d = new Double(.01);
System.out.println(format.format(d));
System.out.printf("%1.1E\n", d);
d = new Double(100);
System.out.println(format.format(d));
System.out.printf("%1.1E\n", d);
Output:
1.0E-2
1.0E-02
1.0E2
1.0E+02
If you need to output to a String instead, you can use the information provided at Formatted Printing for Java (sprintf) to do that.
EDIT: Wow, that PrintfFormat() thing is huge and seems to be unnecessary:
OutputStream b = new ByteArrayOutputStream();
PrintStream p = new PrintStream(b);
p.printf("%1.1E", d);
System.out.println(b.toString());
I got the idea for the above code from Get an OutputStream into a String.
How to use?
See formatTest method.
if (value.compareTo(positive) == 1 || value.compareTo(negative) == -1) is useful for very large numbers
/**
* inspired by:<br>
* https://stackoverflow.com/a/13065493/8356718
* https://stackoverflow.com/a/18027214/8356718
* https://stackoverflow.com/a/25794946/8356718
*/
public static String format(String number, int scale) {
BigDecimal value = new BigDecimal(number);
DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(Locale.US);
BigDecimal positive = new BigDecimal(1);// scale is zero
positive.setScale(0);// unnecessary
BigDecimal negative = new BigDecimal(-1);// scale is zero
negative.setScale(0);// unnecessary
if (value.compareTo(positive) == 1 || value.compareTo(negative) == -1) {
symbols.setExponentSeparator("e+");
} else {
symbols.setExponentSeparator("e");
}
DecimalFormat formatter = new DecimalFormat("0.0E0", symbols);
formatter.setRoundingMode(RoundingMode.HALF_UP);
formatter.setMinimumFractionDigits(scale);
return formatter.format(value);
}
/**
* set the scale automatically
*/
public static String format(String number) {
BigDecimal value = new BigDecimal(number);
return format(number, value.scale() > 0 ? value.precision() : value.scale());
}
/*
output:
----------
0e0
1.0e-2
-1.0e-2
1.234560e-5
-1.234560e-5
1e0
-1e0
3e+0
-3e+0
2e+2
-2e+2
----------
0.0000000000e0
1.0000000000e-2
-1.0000000000e-2
1.2345600000e-5
-1.2345600000e-5
1.0000000000e0
-1.0000000000e0
3.0000000000e+0
-3.0000000000e+0
2.0000000000e+2
-2.0000000000e+2
----------
*/
public static void formatTest() {
System.out.println("----------");
System.out.println(format("0"));
System.out.println(format("0.01"));
System.out.println(format("-0.01"));
System.out.println(format("0.0000123456"));
System.out.println(format("-0.0000123456"));
System.out.println(format("1"));
System.out.println(format("-1"));
System.out.println(format("3"));
System.out.println(format("-3"));
System.out.println(format("200"));
System.out.println(format("-200"));
System.out.println("----------");
System.out.println(format("0", 10));
System.out.println(format("0.01", 10));
System.out.println(format("-0.01", 10));
System.out.println(format("0.0000123456", 10));
System.out.println(format("-0.0000123456", 10));
System.out.println(format("1", 10));
System.out.println(format("-1", 10));
System.out.println(format("3", 10));
System.out.println(format("-3", 10));
System.out.println(format("200", 10));
System.out.println(format("-200", 10));
System.out.println("----------");
}
Why not use "0.0E+0" pattern instead? Note the plus sign before last zero.
Related
This question already has answers here:
Problems using DecimalFormat
(6 answers)
Closed 2 years ago.
I am making a calculator app.
workingsTV is the place where calculating is shown.
resultsTV is the place showing the result of calculating.
workings is doing math by using rhino's library.
I want to add a comma at every three digits on both workingsTV and resultsTV.
I tried to use it like this for resultsTV.
DecimalFormat df = new DecimalFormat("###,###.####", new DecimalFormatSymbols(Locale.US));
result = Double.parseDouble(df.format(result));
But then the app was closed when to show result
This is the error message
Caused by: java.lang.reflect.InvocationTargetException
Caused by: java.lang.NumberFormatException: For input string: "1,235"
Here is the top part of the code
public class MainActivity extends AppCompatActivity {
TextView workingsTV;
TextView resultsTV;
String workings = "";
String CLEAR_INT_TEXT;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initTextView();
}
private void initTextView()
{
workingsTV = (TextView)findViewById(R.id.workingsTextView);
resultsTV = (TextView)findViewById(R.id.resultTextView);
}
private void setWorkings(String givenValue)
{
workings = workings + givenValue;
workingsTV.setText(workings);
}
public void equalsOnClick(View view)
{
Double result = null;
ScriptEngine engine = new ScriptEngineManager().getEngineByName("rhino");
try {
result = (Double) engine.eval(workings);
if (result != null)
{
DecimalFormat df = new DecimalFormat("###,###.####", new DecimalFormatSymbols(Locale.US));
result = Double.parseDouble(df.format(result));
int intVal = (int) result.doubleValue();
if (result == intVal)
{//Check if it's value is equal to its integer part
resultsTV.setText(String.valueOf(intVal));
}
else
{
resultsTV.setText(String.valueOf(result));
}
}
}
I'm using that function to convert double to formatted string
public String formatDouble(double value, int digits) {
DecimalFormatSymbols decimalFormatSymbols = new DecimalFormatSymbols();
decimalFormatSymbols.setGroupingSeparator(',');
decimalFormatSymbols.setDecimalSeparator('.');
DecimalFormat decimalFormat = new DecimalFormat("###,##0.00", decimalFormatSymbols);
decimalFormat.setMinimumFractionDigits(digits);
decimalFormat.setMaximumFractionDigits(digits);
return decimalFormat.format(value);
}
In your code, you already have an result value here result = (Double) engine.eval(workings);. Why do you want get it second time? In addition, using formatted string, who may contains illegal character for double (comma char).
Just remove that two lines
DecimalFormat df = new DecimalFormat("###,###.####", new DecimalFormatSymbols(Locale.US));
result = Double.parseDouble(df.format(result));
And format result value when you'll set it to TextView, example with my function:
resultsTV.setText(formatDouble(result, 4));
At the end of equalsOnClick() method, you should set result or intVal to the workings variable to make it ready for next operations.
workings = String.valueOf(result);
Try this:
import java.text.NumberFormat;
import java.util.Locale;
Locale locale = new Locale("en", "US");
NumberFormat fmt = NumberFormat.getCurrencyInstance();
System.out.println(fmt.format(1235.00));
Regex master needed!
I have an variable timestamp coming from the server and I need to find which format is used everytime. I've tried implementing a regex formats but they don't work. I'm fairly new to regex patterns but still I've tried working them up my self or else look for a specific example but couldn't find so I'm asking you.
The formats from the server can look like this:
"2015-02-23 15:27:31 UTC"
or
"2015-01-22T19:38:40Z"
here is the code to find the formats:
private static String getFormat(String time) {
String firstRegEx = "^\\d{4}-\\d{2}-\\d{2}\'T+\'\\d{2}:\\d{2}:\\d{2}\'Z\'$";
String secondRegEx = "^\\d{4}-\\d{2}-\\d{2}\\s\\d{2}:\\d{2}:\\d{2}\\s\\w{3}$";
if (time.toLowerCase().matches(firstRegEx)) {
return firstRegEx;
} else if (time.toLowerCase().matches(secondRegEx)) {
return secondRegEx;
}
return null;
}
Can you look at my regex patterns and tell me what am I doing wrong?
First you have to remove the single quotes arround the char Tand Zand second you call toLowercase() wich will canvert T to t and Z to z. remove it:
private static String getFormat(String time) {
String firstRegEx = "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$";
String secondRegEx = "^\\d{4}-\\d{2}-\\d{2}\\s\\d{2}:\\d{2}:\\d{2}\\s\\w{3}$";
if (time.matches(firstRegEx)) {
return firstRegEx;
} else if (time.toLowerCase().matches(secondRegEx)) {
return secondRegEx;
}
return null;
}
^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$
Your first regex should be simply this.This will match 2015-01-22T19:38:40Z.See demo.
https://regex101.com/r/aI4rA5/4
Your second regex works fine.
I believe this is the alternative solution suggested in the comments...
public static void main(String[] args) {
System.out.println(getFormat("2015-02-23 15:27:31 UTC"));
System.out.println(getFormat("2015-01-22T19:38:40Z"));
}
private static DateFormat getFormat(String time) {
DateFormat format1 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");
DateFormat format2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
if (isFormat(format1, time)) {
return format1;
} else if (isFormat(format2, time)) {
return format2;
} else {
return null;
}
}
private static boolean isFormat(DateFormat format, String candidate) {
return format.parse(candidate, new ParsePosition(0)) != null;
}
If you were using the regex to decide how to parse later on you could bundle this into a single method capable of consuming multiple formats...
public static void main(String[] args) {
System.out.println(getDate("2015-02-23 15:27:31 UTC"));
System.out.println(getDate("2015-01-22T19:38:40Z"));
}
private static Date getDate(String time) {
DateFormat[] formats = { new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX"),
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z") };
Date date = null;
for (DateFormat format : formats) {
if ((date = format.parse(time, new ParsePosition(0))) != null) {
break;
}
}
return date;
}
If these are the only possible formats then it is enough to test if date.charAt(10) == 'T'
I've been trying to use java.text.DecimalFormat to validate user input of decimal numbers. I know the maximum number of digits both before and after the decimal place that are to be allowed - if there are more of either, that's an input error by the user. It seems like setMaximumIntegerDigits() is not being enforced though. (Or if I use a pattern, rather than the min/max setters, the maximum digits implied by the pattern seem again to be ignored.)
What am I missing?
(Apologies if this is a duplicate. I can find a lot of questions about DecimalFormat on SO - but none that seem to be quite this problem.)
Here's the sample code and a unit test (distilled from real code):
---- Numbers.java
/** Utility for creating and using a standard DecimalFormat. */
package com.myco.util;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.ParsePosition;
public final class Numbers {
private Numbers() { ; }
public static DecimalFormat standardFormat() {
DecimalFormat df = new DecimalFormat();
df.setParseBigDecimal(true);
df.setMaximumIntegerDigits(3);
df.setMinimumIntegerDigits(1);
df.setMaximumFractionDigits(2);
df.setMinimumFractionDigits(0);
return df;
}
// Alternatively, approach, also doesn't work.
/*public static DecimalFormat standardFormat() {
DecimalFormat df = new DecimalFormat("##0.##");
df.setParseBigDecimal(true);
return df;
}*/
public static BigDecimal match(DecimalFormat df, String s)
throws NumberFormatException {
if(s == null || (s = s.trim()).length() == 0)
return null;
ParsePosition pp = new ParsePosition(0);
Number n = df.parse(s, pp);
if(n == null || pp.getIndex() != s.length())
throw new NumberFormatException(
"Invalid BigDecimal for format '"+df+"'!");
return BigDecimal.class.cast(n);
}
}
By my reading of the Javadoc for DecimalFormat the test for 1000 (below) should pass
(that is, it should throw an exception) but it doesn't.
http://docs.oracle.com/javase/7/docs/api/java/text/DecimalFormat.html
---- Numbers_Test.java
package com.myco.util;
import java.text.DecimalFormat;
import junit.framework.TestCase;
public final class Numbers_Test extends TestCase {
public void testOkay() {
DecimalFormat df = Numbers.standardFormat();
assertEquals("0.1", Numbers.match(df, "0.1").toString());
assertEquals("4.2", Numbers.match(df, "4.2").toString());
assertEquals("999.99", Numbers.match(df, "999.99").toString());
}
public void testFail() {
DecimalFormat df = Numbers.standardFormat();
try {
// Returns 1000 (as BigDecimal) but should fail (?).
Numbers.match(df, "1000");
fail();
}
catch(NumberFormatException e) { ; }
}
}
i try to save my String value (50000000) into Double format, while I'm trying to show it again in my Edittext, I can't to show it in normal format, and it show as (5E+07), is there any way to convert from double format into String format?
I have try this way :
Double value_doble = 5E+07;
EditText.setText(String.valueOf(value_doble);
but its Still show as 5E+07, so my question how to convert from Double to String?
You can try this:
System.out.println(new BigDecimal(value_doble).toString());
Is this what you are looking for?
public static void main(String[] args) {
Double value_doble = 5E+07;
NumberFormat formatter = new DecimalFormat("###.#####");
String f = formatter.format(value_doble);
System.out.println(f);
}
I agree that you need use Formater
http://docs.oracle.com/javase/tutorial/i18n/format/decimalFormat.html
but the pattern should be look like this:
import java.text.DecimalFormat;
import java.text.NumberFormat;
public class DoubleFormat {
public static void main(String[] args) {
double valueD = 5E+07;
NumberFormat format = new DecimalFormat("#");
System.out.println(format.format(valueD));
}
}
I'm making an Android Java program which is taking double values from the user. If I run the program on the computer, it works great because of the locale of my computer, EN_UK. But when I run it on my mobile phone with FI_FI locale, it won't work. I know the reason: In UK, people use dot as decimal separator but here in Finland, the decimal separator is comma.
DecimalFormat df = new DecimalFormat("#.#");
Double returnValue = Double.valueOf(df.format(doubleNumber));
When I'm using comma, it says java.lang.NumberFormatException: Invalid double: "1234,5".
How can I make it work with them both, comma and dot?
Use one of the other constructors of DecimalFormat:
new DecimalFormat("#.#", new DecimalFormatSymbols(Locale.US))
And then try and parse it using both separators.
using DecimalFormatSymbols.getInstance() will produce the default locale's correct symbols, so you will get it right for any platform you run on.
DecimalFormat df = new DecimalFormat("#.#", DecimalFormatSymbols.getInstance());
This should work for both Java(Tested) as well as android :)
Class Name: In18Helper.java
package com.akmeher.app.utils;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Locale;
public class In18Helper {
private final static In18Helper mHelper = new In18Helper();
public static final In18Helper getInstance() {
return mHelper;
}
public double getDouble(String sValue, Locale locale) {
NumberFormat numberFormat = NumberFormat.getInstance(locale);
Number parse = null;
try {
parse = numberFormat.parse(sValue);
} catch (ParseException e) {
e.printStackTrace();
}
return parse == null ? 0 : parse.doubleValue();
}
}
Class Name: Application.java
package com.akmeher.app;
import java.util.Locale;
import com.akmeher.app.utils.In18Helper;
public class Application {
static DataModel[] testData = new DataModel[] {
new DataModel("1.034567", Locale.ENGLISH),
new DataModel("1,0345.67", Locale.ENGLISH),
new DataModel("1.0345,67", Locale.GERMANY),
new DataModel("1,034,567", Locale.CANADA),
new DataModel("1.034567", Locale.KOREA),
new DataModel("1,03.4567", Locale.ITALY) };
/**
* #param args
*/
public static void main(String[] args) {
for (int i = 0; i < testData.length; i++) {
double d = In18Helper.getInstance().getDouble(testData[i].mValue,
testData[i].mLocale);
System.out.println("Trial Value: "+testData[i].mValue+" for Locale: "+testData[i].mLocale+" converted to: "+d);
}
}
private static class DataModel {
String mValue;
Locale mLocale;
public DataModel(String value, Locale locale) {
this.mLocale = locale;
this.mValue = value;
}
}
}
Output:
Trial Value: 1.034567 for Locale: en converted to: 1.034567
Trial Value: 1,0345.67 for Locale: en converted to: 10345.67
Trial Value: 1.0345,67 for Locale: de_DE converted to: 10345.67
Trial Value: 1,034,567 for Locale: en_CA converted to: 1034567.0
Trial Value: 1.034567 for Locale: ko_KR converted to: 1.034567
Trial Value: 1,03.4567 for Locale: it_IT converted to: 1.03
Hope this will help somebody to make use of.
public static Double parseDoubleTL(String value){
DecimalFormat df = new DecimalFormat("#.#", new DecimalFormatSymbols(new Locale("tr_TR")));
Double doublePrice = 0.0;
try {
doublePrice = df.parse(value).doubleValue();
} catch (ParseException e) {
Log.w(MainActivity.TAG,"Couldnt parse TL. Error is "+e.toString());
}
return doublePrice;
}
Not a best way but worked for me;
Double val=null;
try{
val=Double.valueOf(value);
}catch(Exception e){
val=Double.valueOf(value.replace(',','.'));
}
Double val=null;
try{
val=Double.valueOf(value);
}catch(Exception e){
val=Double.valueOf(value.replace(',','.'));
}
return val;
Me Error:
java.lang.NumberFormatException: Invalid float: "1,683.88"
... and this work for me
replace(",", "")
DecimanFormat df = new DecimalFormat("#.#");