I've been reading up on the net about the issues with handling float and double types in java. Unfortunately, the image is still not clear. Hence, i'm asking here direct. :(
My MySQL table has various DECIMAL(m,d) columns. The m may range from 5 to 30. d stays a constant at 2.
Question 1.
What equivalent data-type should i be using in Java to work (i.e store, retrieve, and process) with the size of the values in my table? (I've settled with double - hence this post).
Question 2.
While trying to parse a double from a string, i'm getting errors
Double dpu = new Double(dpuField.getText());
for example -
"1" -> java.lang.NumberFormatException: empty String
"10" -> 1.0
"101" -> 10.0
"101." -> 101.0
"101.1" -> 101.0
"101.19" -> 101.1
What am i doing wrong? What is the correct way to convert a string to a double value?
And what measures should i take to perform operations on such values?
EDIT
This is the code -
System.out.println(dpuField.getText());
Double dpu = new Double(dpuField.getText());
System.out.println(dpu);
Yes, the problem lies with getText() reporting the wrong value of the dpuField.
This method is called on the JTextField keyTyped event. So what's going wrong here?
EDIT 2
Looking at :
http://journals.ecs.soton.ac.uk/java/tutorial/post1.0/ui/keylistener.html
Apparently, keyTyped() does not give me the keycode. I'll have to switch to keyRealeased()
What equivalent data-type should i be using in Java to work (i.e store, retrieve, and process) with the size of the values in my table? (I've settled with double - hence this post).
Since it's a DECIMAL field, you should prefer java.math.BigDecimal. You can store it in DB using PreparedStatement#setBigDecimal() and you can retrieve it from DB using ResultSet#getBigDecimal().
While trying to parse a double from a string, i'm getting errors
This can't be true. The problem lies somewhere else. Maybe it is just not returning the data you expect to be returned or you are not using/debugging the values you expect them to be.
if you need exact precision without rounding errors, you should use a BigDecimal.
Your code looks OK - could it be that dpuField.getText() somehow cuts the last character from the string values you list above?
Update: you say
Yes, the problem lies with getText() reporting the wrong value of the dpuField. This method is called on the JTextField keyTyped event.
Could it be that getText() returns the value of the field before the last typed key is actually appended to it?
For decimal, I believe you risk losing precision if you don't use a BigDecimal on the Java side, as some decimal fractions can't be stored as a binary fraction.
Prefer Double.valueOf(String) over the constructor, but that's a valid way. Something else must be going on (i.e. I doubt those are the actual String values you're passing in).
Question1: It's bad idea to map DECIMAL columns to Double, usually the BigDecimal is the correct type. http://java.sun.com/j2se/1.3/docs/guide/jdbc/getstart/mapping.html#1055175
Question 2: You are doing something wrong; print the String value before converting.
Related
This question already has answers here:
Java BigDecimal can have comma instead dot?
(3 answers)
Closed 3 months ago.
I have a method to set a BigDecimal number that is given as String:
private Client mapClient(Client client){
ClientRequest clientRequest = new ClientRequest();
// Code
clientRequest.setCashAmount(castStringToBigDecimal(client.getCashAmount()));
// More Code
}
My castStringToBigDecimal method is the follosing:
public BigDecimal castStringToBigDecimal(String value){
BigDecimal response = null;
if(value != null && !value.equals("")){
value = value.replaceAll("[.]", ",");
response = new BigDecimal(value);
}
return response;
}
An example of the input value is "1554.21"
I need that the bigDecimal separator to be a comma, not a dot. But this is giving me an exception.
EDIT
The value is the following:
And the exception is:
java.lang.NumberFormatException: Character , is neither a decimal digit number, decimal point, nor "e" notation exponential mark.
BigDecimal doesn't represent a rendering. In other words, whether to use a comma or a dot as separator is not part of the properties a BigDecimal object has.
Hence, you do not want to call .replaceAll. (And separately, you'd want .replace(".", ",") - replace replaces all, and replaceAll also replaces all and interprets the first arg as a regex, and is therefore needlessly confusing here). Just pass it with the dot.
To render a BigDecimal, don't just sysout it, that will always show a dot and there is nothing you can do about that. toString() is almost never the appropriate tool for the job of rendering data to a user - it's a debugging aid, nothing more. Use e.g. String.format("%f"), specifying the appropriate locale. Or use NumberFormat. The javadoc of BigDecimal explicitly spells this out.
There are various other issues with your code:
"cast" is the technical name for the syntactic construct: (Type) expr; - and this construct does 3 utterly different things, hence using it to describe a task, i.e. use it in a method name, is a very bad idea. In particular, only one of the 3 things it does converts anything, and you clearly use it here in the 'convert something' meaning. This is misleading; only if it's all primitives does the cast operator convert, and BigDecimal isn't primitive. Call it convertTo or whatever you please, not "cast".
BigDecimal is an extremely complicated tool for the job and usually not the right tool if you want to represent financial data. Instead, represent the atomary unit in a long and call the appropriate rendering method whenever you need to show it to a user. For example, for euros, the atomary unit is the eurocent. If something costs €1,50, you'd store "150", in a long. Before you think: But, wait, I want to divide, and then I'd lose half a cent! - yes, well, you can't exactly send your bank a request to transfer half a cent, either. Also, try to divide 4 cents by 3 with a BigDecimal and see what happens. Dividing financial amounts is tricky no matter what you use, BD isn't a catch-all solution to this problem.
I looked up the source code for Java 8's implementation of BigDecimal (https://github.com/frohoff/jdk8u-dev-jdk/blob/master/src/share/classes/java/math/BigDecimal.java), and the period character is hard-coded in that source as the decimal point. I would not have thought this of a language for which internationalization has been so thoroughly designed in, but there it is, line 466.
Given that the author(s) of BigDecimal failed to take locale into account in such a basic way -- the use of comma instead of period as the decimal separator in Europe is well-known -- I'd have to say you cannot use that BigDecimal constructor on unaltered Strings that are otherwise formatted correctly but which (might) have a comma separator. There are other options -- the previous SO post referred to in one of the comments has one -- but it appears you cannot convert your String this way.
(One minor point -- you are not "casting" anything. That word has a specific meaning in OO programming, and a more specific one in Java, and has very little to do with your question. It is incorrect to refer to conversion as casting.)
I have an object from an implemented class ReportManager. Now getReport() is a number like 0.23 with the data type report. But I want this number to be a double so I can work with it.
I cannot change the class, because it is implemented in the Java compiler (it is for writing macros for a program).
Does anybody have a suggestion how I could handle it? I checked the API and there is no function implemented that could help me.
EDIT: I do have the situation: I want to calculate the Center of Pressure of an object in my simulation. So I need the moment in that position to be 0.
Now: This is how the automated macro ask the value of the Moment:
MomentReport momentReport_0 =
((MomentReport) simulation_0.getReportManager().getReport("Moment 1"));
Now I want to take the abs of it, because I don't mind if it's positive or negative.
while(Math.abs(momentReport_0) > 0.2)
(Do iterate and change position.) At the end I want to println the the position.
simulation_0 is an object of Simulation. I could copy a part of the API if it's needed. Just don't know which class documentation would help.
You can cast the number to a double so that you can work with it, assuming it's returning you a single-precision float at the moment.
double result = (double) reportManager.getReport();
I recommend you read up on what typecasting is so that you can better understand what's going on here, as there would be some situations where it's unsafe to cast:
https://en.wikipedia.org/wiki/Type_conversion
for some reason I found myself coding some piece of software, that should be able to perfom some astronomic calculations.
While most of it will be about transfering the correct formula into Java, I found an annoying Problem right at the verry beginning of my "test how to calculate big numbers".
Well... Imagine the Sun (our Sun), which has a mass of (about and rounded, for more easy explaining) 10E30 kg. Ten with 30 following Zeros. All native datatypes are just unusuable for this. To mention: I KNOW that I could use 3000 to calculate things and just add trailing zeros in the output-view, but I hoped to keep it as precise as possible. So using short numbers will be my last resort only.
Comming to the Problem. Please have a look at the code:
BigDecimal combinedMass = new BigDecimal(1E22);
int massDistribution = 10;
Integer mD1 = massDistribution;
Integer mD2 = 100 - massDistribution;
BigDecimal starMass;
BigDecimal systemMass;
systemMass = combinedMass.divide(new BigDecimal("100")).multiply(new BigDecimal(mD1.toString()));
starMass = combinedMass.divide(new BigDecimal("100")).multiply(new BigDecimal(mD2.toString()));
System.out.println((systemMass).toEngineeringString());
System.out.println((starMass));
It will output 1000000000000000000000 and 9000000000000000000000, whats exactly what I did expect. But look at the combineMass Field. If I raise it to 1E23, the Output will change
I get 9999999999999999161139.20 and 89999999999999992450252.80...
So I know I could use jut BigInteger, because its more reliable in this case, but for the sake of precicion, sometimes the BigWhatEver may drop to something like 50.1258
Plus, I hope to get the 10.xE30 as output, whats only possible using bigDecimals.
I want to know: Is there no way avoidng this (that error appers above 1E23 for every value I tried), while keeping the ability to calculate Floating-Points? Should I cut the After-Decimal-Separator-Values for this Field to two digets?
And for something more to wonder about:
System.out.println(combinedMass.precision());
in relation with the code above will provide 23 for that case, but En+1 for most other values (Thats was when I grow really confused)
Thanks for advise.
You're using basic types without realizing it:
new BigDecimal(1E22);
Here, 1E22 is a primitive double, and you already lost precision by using it.
What you want is
new BigDecimal("10000000000000000000000");
or
new BigDecimal(10).pow(22);
...
float value = Float.parseFloat((String)model.getValueAt(e.getLastRow(), 1));
DecimalFormat dec = new DecimalFormat("#.###");
model.setValueAt(dec.format(value), e.getLastRow(), 1);
...
at the third line i'm getting the stackOverflowError exception. What I'm intending to do is getting a JTable cell value from an Object, converting it to a float, limiting it to 3 decimal places, and finally convert to String and set the value with 3 decimal places at the cell.
I guess the problem is I'm changing the value, and entering the function again and again. So the StackOverflow is due to that. Question is, how can i fix this?
Complete function at: Java: Converting data types
(Sorry for posting twice... It was a different question, and the solution drove me to a different problem)
The problem is that setValueAt() will, as part of its implementation call tableChanged() on all registered listeners, including this one.
In order to avoid this, simply check whether the value in the cell is already in the desired format as the first thing in your method, and don't do anything if it is.
Just don't call model.setValueAt() if value of the cell is not changed.
It should stop the recursion.
I think this task is usually accomplished by setting a custom editor to the table. So that it formats all input data to a desired form. See this answer.
Perhaps you need something like
String text = (String) model.getValueAt(e.getLastRow(), 1);
String text2 = new DecimalFormat("#.###").format(Float.parseFloat(text));
if (!text.equals(text2))
model.setValueAt(dec.format(value), e.getLastRow(), 1);
I am aware of the various posts floating out there with regards to the same issue.
Mine its a little bit different and it might be a little obvious, but I will need your comments.
I am currently using Hibernate Search and Lucene to Index entity properties.
I have a bunch of Double properties on my entities.
These entities using the default Bridges from Lucene (Bridge i.e the one in charge converting LongToString and StringToLong) are giving me troubles once the scientific notation starts to be used.
I am trying to show on DataTables on a .xhtml Credit and Debit amounts, their lenght can be as long as 18 digits, and their DataBase (DB2) type is BIGINT.
I can not change the DataBase type
to Long for example.
I can not change either the Double
type attributes of my entities
either to for example Long
So whats the question?
Is there a way from a String say "1234567890" to retrieve a Double whose format is 1234567890 and not 1.23456789E9 as it is being done by default by Double.parseDouble(FormattedString)?
PD: I am aware of the existance of DecimalFormat, however take into account using this formater will give me a String formated correctly say : "#######.E0" but what I really need is a Double with such format, however when doing Double.parseDouble(FormattedString) I will loose such format.
Hope I was clear and thanks for any help.
Is there a way from a String say "1234567890" to retrieve a Double whose value is 1234567890 and not 1.23456789E9 as it is being done by default by Double.parseDouble(FormattedString)?
Your question doesn't really make sense. 1234567890 is the same value as 1.23456789E9 and a double represents one of them, if and only if it also represents the other.
I am aware of the existance of DecimalFormat, however take into account using this formater will give me a String formated correctly say : "#######.E0" but what I really need is a Double with such format, however when doing Double.parseDouble(FormatedString) I will loose such format.
No, there is no way to construct a Double so that it is displayed in a certain way. The toString method for Double is what it is, and it can't be changed.
The only thing you can do is to for instance use DecimalFormat or String.format but as you've noted, you'll always end up with a String.
Don't know nothing of Lucene, but you can never have a Double in a .xhtml Document it is always a characterstring. A Double doesn't have a Format, only a String representation of a Double has.
So I finally got the solution to my problem.
After rounding up what aioobe and Jens Schauder said. I am able to format the text dynamically on my .xhtml with the following tag:
<h:outputText value="#{recordTable[column.property]}"
rendered="#{column.header ne 'Details' and
column.header eq ('Total Credit Amount' or
'Total Debit Amount')}">
<f:convertNumber pattern="########"/>
</h:outputText>
Thanks for making clear to me these basic stuff I had blurred :)