DecimalFormat: No leading zero, no exponential, variable precision - java

I'm using an Oracle DB together with JDBC. I have to select some values and write them to a file.
First I used ResultSet#getString which gave me almost all values formatted as desired except sometimes numbers were represented with an exponential. E.g.: 0.0897234E-4
In order to get rid of the exponential I checked if the type of the current column is NUMERIC and then used ResultSet#getBigDecimal to get a BigDecimal. I've done that because there are all kinds of Java number types stored in the DB and as Oracle DB only provides NUMERIC as type for numbers, I have to use the BigDecimal because every numeric Java type can be stored in a BigDecimal.
Then I used the BigDecimal#toPlainString method to effectively get rid of the exponential.
if (rset.getMetaData().getColumnType(i) == java.sql.Types.NUMERIC) {
value = (rset.getBigDecimal(i) != null rset.getBigDecimal(i).toPlainString() : "0");
}
The next problem was, that I then got a leading zero when the number was below 1 but I don't want them.
Before .007346 <-- desired
After 0.007346
Before -.4352 <-- desired
After -0.4352
To deal with that problem I searched the internet and found the DecimalFormat class. I was then able to format the numbers so that I don't have leading zeros with the following format:
new DecimalFormat("#.");
But this of course didn't display any digits after the decimal point but does add a decimal point after every number. E.g.: 1235. -65.
So what I want is, that if the number is decimal there should be as much digits as needed (actually 38, I think is in Oracle DB the max). There should never be exponentials and if the number is below 1 there shouldn't be a leading zero. If the number is natural there shouldn't be a decimal point at the end.
Some examples of the desired format:
9873478 -1349 .743803 -.004726
How can I achieve such a representation? Any solution is welcome I don't have to use the DecimalFormat. Could the solution possibly be, that I have to determine the type after getting the number as a BigDecimal by trying to convert it to the different Java types?

What about new DecimalFormat(".#");?
You were very close to the answer, indeed. As you can see the behaviour of the decimals in your attempt is the behaviour you expected on the integer part.
As you can read in Javadoc # shows zero as absent
EDIT
As stated in the comments, the problem with the solution above is that you get only one decimal. You'd maybe better define your layout by the API rather than with a pattern.
DecimalFormat format = new DecimalFormat();
format.setMinimumIntegerDigits(0);
format.setMaximumFractionDigits(2000);//should be more than enough
Note that you could also improve the first solution
DecimalFormat format = new DecimalFormat(".##################################################################");
...but it is less easy to define a large number of fraction digits (if you really need it).

Related

decimal points precision is lost for some munbers in case of double data type

I am suprise to see the below program please advise how it is behaving like this as i aam very much concerened with poitn to precison after decimal point , below is the progrmam
double fixedRate = 0.997500000000; //**output --->0.9975
// BigDecimal fixedRate = new BigDecimal("0.997500000000");
double fixedRate1 = 0.1234567890123456789;
System.out.println(fixedRate);
System.out.println(fixedRate1);
and the output is
0.9975
0.12345678901234568
now please advise for the first the ouput is 0.9975 but late on for next it is not truncating after decimal points but why for first then.
The precision is not lost. It is just not printed because you do not need more digits to distinguish the printed value from any other double value.
If you do want to force a certain number of fractional digits, take a look at System.out.printf() or String.format().
See https://docs.oracle.com/javase/7/docs/api/java/util/Formatter.html for more details and possible formats.
The result may look like this:
System.out.printf("%.19f%n",fixedRate);
System.out.printf("%.19f%n", fixedRate1);
This is a printing problem.Please use this format to print your values and tell me the result:
double fixedRate = 0.997500000000;
double fixedRate1 = 0.1234567890123456789;
System.out.println(String.format("%.19f", fixedRate ));
System.out.println(String.format("%.19f", fixedRate1 ));
Good Luck !
According to your question
for the first the ouput is 0.9975 but late on for next it is not
truncating after decimal points but why for first then
Since double is numeric datatype and hence cannot hold the leading and trailing zeros.
A double doesn't care about formatting - it's about storage only. When you print it, it is converted to a String (using Double's static toString method).
In simple terms the value 0.9975 is not different from 0.997500000000or it is same as 0.997500000000 as zeros after a number will not have any value.
But consider if you had value like this 0.9975000000001 then all the numbers will be printed. Check it here.
If you want to format the value then you can see this question : How to properly display a price up to two decimals (cents) including trailing zeros in Java?

VoltDB - decimal values have too many zeroes

when I try to add a decimal value to a column in voltdb, it always adds extra zeroes to the decimal. The column type is DECIMAL, which equates to Java's BigDecimal type. Even if I format the BigDecimal value in java to a two decimal place BigDecimal before doing the insert, it still shows up with lots of trailing zeroes in the column.
Any idea how to fix this?
Thanks
DECIMAL columns in VoltDB are stored as 16-bytes with a fixed scale of 12 and precision of 38. The range of values is from -99999999999999999999999999.999999999999 to 99999999999999999999999999.999999999999.
When you say "it still shows up with lots of trailing zeros" you may be seeing the way one of the interfaces displays DECIMAL values by default. You can control formatting in your own client in various ways depending on what language you are using. You may also used the FORMAT_CURRENCY() SQL function to convert a DECIMAL value to a string representation with a given number of decimal places.

How to do arithmetic operations in binary with Java?

For a Java assignment I am required to be able to pass any number that will be introduced as a string through the command line (no matter how big) into binary.
Then generate methods that will allow these numbers to add, multiply, subtract and divide.
My question would be first:
How do I make my string into binary
Eg:
123 would become 1111011
8403678 would become 100000000011101011011110
And so forth...
Then the biggest issue is to get them to add up, subtract each other, etc.
Last I need to be able to convert back the result from binary back to decimal which I am having more trouble understanding how to do it than the previous case (transforming from binary into a decimal string).
Eg:
if 1111011 was added to 100000000011101011011110 the result would be 100000000011101101011001 and then it would become 8403801 which I would print out as a result.
The final aim of this project is to create our own class such as java.math.BigInteger (without using it of course) and handling arbitrarily big numbers (bigger than what Int can handle).
If there is any extra information required please let me know I will answer promptly.
Since you have to be able to handle large numbers without using BigInteger, you need to find a way to represent arbitrarily large numbers. Obviously int will not do. One easy way is to represent the number as a String. For instance, the number 123 could be stored as the String "123".
Converting to binary will require some intermediate operations such as division and modulo. Thus, it is worth thinking about how to do these when your numbers are stored in Strings. Since this is homework I don't want to just give you the answer, but some guidance instead.
Say you want to do addition.
Think about how you add big numbers by hand. Which digits of each number do you use, and how do you manipulate them to get the answer? This algorithm is fairly straightforward, and once you can explain it, you can give a computer directions to do it as well. (For addition, you add first the one's digits, then the ten's digits, etc... and remember to carry if you have to!)
Note that you can get the digits of your number String by using a method such as charAt(int n). This will return the character at index n of the string. Convert it to an Integer by using Integer.parseInt() (which takes a numeric string and converts it to an integer).
So now you can think: If I want the one's digit of a number, what index would that be in a String? Starting with this, you should be able to figure out how to get any digit you want from a big number string. Now, you can implement your algorithm.
Finally, to convert from base ten to binary you do need to understand how number bases work. This gives a clear and quick introduction: http://www.math.grin.edu/~rebelsky/Courses/152/97F/Readings/student-binary
The section "Converting from decimal to binary" in the above link describes a method for exactly what you want to do. Good luck.

How do I handle floating point value inputs for values greater than Float.MAX_VALUE

I have an api which takes a number as a String input and i need to get the Float value of the number. I currently use the Float.ParseFloat method to get the float value of my String number.
According the java documentation of Float.ParseFloat, it doesn't mention anything about the input being greater than the Float.MAX_VALUE.
One of the ways I was thinking of doing this was by checking the length of the input String is greater than the length of the Float.MAX_VALUE.
Pls suggest how I can go about handling this.
Although the javadoc doesn't make it clear, when I tested it, parseFloat of a String too large simply produced a Float of 'infinity'. You could use the isInfinite() method after creation to check the value.
Using something like BigDecimal would probably be a safer option here, especially if you'll be performing any arithmetic on your value.
You can use greater precision. Try double or BigDecimal. There are also arbitrary precision libraries which are open.
Here you can find how much each IEEE 754 format can hold: http://en.wikipedia.org/wiki/IEEE_754-2008 . Float would be near 1.234567*10^38
If you can't parse it properly (e.g. if there are too many significant digits or the exponent is too big: 1.23456789012345e5000) you won't be able either to hold it in a single precision float.
If the number is too big the result is set to Float.POSITIVE_INFINITY, as the rules of IEEE FP arithmetic require, and as a 10-second test shows.
The exponent clips to the maximum exponent value. See the source, line 1197.
Perhaps check for some maximum useful value for your application?

Precision nightmare in Java and SQL Server

I've been struggling with precision nightmare in Java and SQL Server up to the point when I don't know anymore. Personally, I understand the issue and the underlying reason for it, but explaining that to the client half way across the globe is something unfeasible (at least for me).
The situation is this. I have two columns in SQL Server - Qty INT and Price FLOAT. The values for these are - 1250 and 10.8601 - so in order to get the total value its Qty * Price and result is 13575.124999999998 (in both Java and SQL Server). That's correct. The issue is this - the client doesn't want to see that, they see that number only as 13575.125 and that's it. On one place they way to see it in 2 decimal precision and another in 4 decimals. When displaying in 4 decimals the number is correct - 13575.125, but when displaying in 2 decimals they believe it is wrong - 13575.12 - should instead be 13575.13!
Help.
Your problem is that you are using floats. On the java side, you need to use BigDecimal, not float or double, and on the SQL side you need to use Decimal(19,4) (or Decimal(19,3) if it helps jump to your precision level). Do not use the Money type because math on the Money type in SQL causes truncation, not rounding. The fact that the data is stored as a float type (which you say is unchangeable) doesn't affect this, you just have to convert it at first opportunity before doing math on it.
In the specific example you give, you need to first get the 4 decimal precision number and put it in a BigDecimal or Decimal(19,4) as the case may be, and then further round it to 2 decimal precision. Then (if you are rounding up) you will get the result you want.
Use BigDecimal. Float is not an approciate type to represent money. It will handle the rounding properly. Float will always produce rounding errors.
For storing monetary amounts floating point values are not the way to go. From your description I would probably handle amounts as long integers with as value the monetary amount multiplied by 10^5 as database storage format.
You need to be able to handle calculations with amounts that do not loose precision, so here again floating point is not the way to go. If the total sums between debit and credit are off by 1 cent in a ledger, the ledger fails in the eyes of financial people, so make sure your software operates in their problem domain, not yours. If you can not use existing classes for monetary amounts, you need to build your own class that works with amount * 10^5 and formats according to the precision wanted only for input and output purposes.
Don't use the float datatype for
price. You should use "Money" or
"SmallMoney".
Here's a reference for [MS SQL
DataTypes][1].
[1]:
http://webcoder.info/reference/MSSQLDataTypes.html
Correction: Use Decimal(19,4)
Thanks Yishai.
I think I see the problem.
10.8601 cannot be represented perfectly, and so while the rounding to 13575.125 works OK it's difficult to get it to round to .13 because adding 0.005 just doesn't quite get there. And to make matters worse, 0.005 doesn't have an exact representation either, so you end up just slightly short of 0.13.
Your choices are then to either round twice, once to three digits and then once to 2, or do a better calculation to start with. Using long or a high precision format, scale by 1000 to get *.125 to *125. Do the rounding using precise integers.
By the way, it's not entirely correct to say one of the endlessly repeated variations on "floating point is inaccurate" or that it always produces errors. The problem is that the format can only represent fractions that you can sum negative powers of two to create. So, of the sequence 0.01 to 0.99, only .25, .50, and .75 have exact representations. Consequently, FP is best used, ironically, by scaling it so that only integer values are used, then it is as accurate as integer datatype arithmetic. Of course, then you might as well have just used fixed point integers to start with.
Be careful, scaling, say, 0.37 to 37 still isn't exact unless rounded. Floating point can be used for monetary values but it's more work than it is worth and typically the necessary expertise isn't available.
The FLOAT data type can't represent fractions accurately because it is base2 instead of base10. (See the convenient link :) http://gregs-blog.com/2007/12/10/dot-net-decimal-type-vs-float-type/).
For financial computations or anything that requires fractions to be represented accurately, the DECIMAL data type must be used.
If you can't fix the underlying database you can fix the java like this:
import java.text.DecimalFormat;
public class Temp {
public static void main(String[] args) {
double d = 13575.124999999;
DecimalFormat df2 = new DecimalFormat("#.##");
System.out.println( " 2dp: "+ Double.valueOf(df2.format(d)) );
DecimalFormat df4 = new DecimalFormat("#.####");
System.out.println( " 4dp: "+Double.valueOf(df4.format(d)) );
}
}
Although you shouldn't be storing the price as a float in the first place, you can consider converting it to decimal(38, 4), say, or money (note that money has some issues since results of expressions involving it do not have their scale adjusted dynamically), and exposing that in a view on the way out of SQL Server:
SELECT Qty * CONVERT(decimal(38, 4), Price)
So, given that you can't change the database structure (which would probably be the best option, given that you are using a non-fixed-precision to represent something that should be fixed/precise, as many others have already discussed), hopefully you can change the code somewhere. On the Java side, I think something like #andy_boot answered with would work. On the SQL side, you basically would need to cast the non-precise value to the highest precision you need and continue to cast down from there, basically something like this in the SQL code:
declare #f float,
#n numeric(20,4),
#m money;
select #f = 13575.124999999998,
#n = 13575.124999999998,
#m = 13575.124999999998
select #f, #n, #m
select cast(#f as numeric(20,4)), cast(cast(#f as numeric(20,4)) as numeric(20,2))
select cast(#f as money), cast(cast(#f as money) as numeric(20,2))
You can also do a DecimalFormat and then round using it.
DecimalFormat df = new DecimalFormat("0.00"); //or "0.0000" for 4 digits.
df.setRoundingMode(RoundingMode.HALF_UP);
String displayAmt = df.format((new Float(<your value here>)).doubleValue());
And I agree with others that you should not be using Float as a DB field type to store currency.
If you can't change the database to a fixed decimal datatype, something you might try is rounding by taking truncate((x+.0055)*10000)/10000. Then 1.124999 would "round" to 1.13 and give consistent results. Mathematically this is unreliable, but I think it would work in your case.

Categories

Resources