VoltDB - decimal values have too many zeroes - java

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.

Related

Cannot save all decimal places

I have the following column definition in mariadb:
multiplier double
In my entity class, I have the following column:
#Column(name="multiplier", precision=15)
private Double itemMultiplier;
I already specified the precision but still the data saved in the table is limited up to 5 decimal digits only. I logged the value of itemMultiplier and it's 0.458327459574. But in the table, the value saved is just 0.45833.
I also tried using double(22,15) and decimal(22,15) in the column multiplier, it also didn't work.
How can I save all the decimal places? I also tried using BigDecimal for itemMultiplier, it didn't work.
double(22,15) is an abomination. You get unnecessary rounding at the bottom and truncation at the top. If you are going to use DOUBLE, use just plain `DOUBLE, which gives you about 16 significant digits.
"Only 5 decimal places in the output"? Perhaps the formatting rounded to 5 decimal places, not the data.
You say
I already specified the precision...
By what means did you specify the precision?
Please provide the SQL statements involved. In particular SHOW CREATE TABLE. We need to figure out whether Java is messing things up or MariaDB is.
Another thing to try is to display not just multiplier, but 100*multiplier -- report back whether you get 45.83275 or 45.83300.

Is it sufficient to convert a double to a BigDecimal just before addition to retain original precision?

We are solving a numeric precision related bug. Our system collects some numbers and spits their sum.
The issue is that the system does not retain the numeric precision, e.g. 300.7 + 400.9 = 701.599..., while expected result would be 701.6. The precision is supposed to adapt to the input values so we cannot just round results to fixed precision.
The problem is obvious, we use double for the values and addition accumulates the error from the binary representation of the decimal value.
The path of the data is following:
XML file, type xsd:decimal
Parse into a java primitive double. Its 15 decimal places should be enough, we expect values no longer than 10 digits total, 5 fraction digits.
Store into DB MySql 5.5, type double
Load via Hibernate into a JPA entity, i.e. still primitive double
Sum bunch of these values
Print the sum into another XML file
Now, I assume the optimal solution would be converting everything to a decimal format. Unsurprisingly, there is a pressure to go with the cheapest solution. It turns out that converting doubles to BigDecimal just before adding a couple of numbers works in case B in following example:
import java.math.BigDecimal;
public class Arithmetic {
public static void main(String[] args) {
double a = 0.3;
double b = -0.2;
// A
System.out.println(a + b);//0.09999999999999998
// B
System.out.println(BigDecimal.valueOf(a).add(BigDecimal.valueOf(b)));//0.1
// C
System.out.println(new BigDecimal(a).add(new BigDecimal(b)));//0.099999999999999977795539507496869191527366638183593750
}
}
More about this:
Why do we need to convert the double into a string, before we can convert it into a BigDecimal?
Unpredictability of the BigDecimal(double) constructor
I am worried that such a workaround would be a ticking bomb.
First, I am not so sure that this arithmetic is bullet proof for all cases.
Second, there is still some risk that someone in the future might implement some changes and change B to C, because this pitfall is far from obvious and even a unit test may fail to reveal the bug.
I would be willing to live with the second point but the question is: Would this workaround provide correct results? Could there be a case where somehow
Double.valueOf("12345.12345").toString().equals("12345.12345")
is false? Given that Double.toString, according to javadoc, prints just the digits needed to uniquely represent underlying double value, so when parsed again, it gives the same double value? Isn't that sufficient for this use case where I only need to add the numbers and print the sum with this magical Double.toString(Double d) method? To be clear, I do prefer what I consider the clean solution, using BigDecimal everywhere, but I am kind of short of arguments to sell it, by which I mean ideally an example where conversion to BigDecimal before addition fails to do the job described above.
If you can't avoid parsing into primitive double or store as double, you should convert to BigDecimal as early as possible.
double can't exactly represent decimal fractions. The value in double x = 7.3; will never be exactly 7.3, but something very very close to it, with a difference visible from the 16th digit or so on to the right (giving 50 decimal places or so). Don't be mislead by the fact that printing might give exactly "7.3", as printing already does some kind of rounding and doesn't show the number exactly.
If you do lots of computations with double numbers, the tiny differences will eventually sum up until they exceed your tolerance. So using doubles in computations where decimal fractions are needed, is indeed a ticking bomb.
[...] we expect values no longer than 10 digits total, 5 fraction digits.
I read that assertion to mean that all numbers you deal with, are to be exact multiples of 0.00001, without any further digits. You can convert doubles to such BigDecimals with
new BigDecimal.valueOf(Math.round(doubleVal * 100000), 5)
This will give you an exact representation of a number with 5 decimal fraction digits, the 5-fraction-digits one that's closest to the input doubleVal. This way you correct for the tiny differences between the doubleVal and the decimal number that you originally meant.
If you'd simply use BigDecimal.valueOf(double val), you'd go through the string representation of the double you're using, which can't guarantee that it's what you want. It depends on a rounding process inside the Double class which tries to represent the double-approximation of 7.3 (being maybe 7.30000000000000123456789123456789125) with the most plausible number of decimal digits. It happens to result in "7.3" (and, kudos to the developers, quite often matches the "expected" string) and not "7.300000000000001" or "7.3000000000000012" which both seem equally plausible to me.
That's why I recommend not to rely on that rounding, but to do the rounding yourself by decimal shifting 5 places, then rounding to the nearest long, and constructing a BigDecimal scaled back by 5 decimal places. This guarantees that you get an exact value with (at most) 5 fractional decimal places.
Then do your computations with the BigDecimals (using the appropriate MathContext for rounding, if necessary).
When you finally have to store the number as a double, use BigDecimal.doubleValue(). The resulting double will be close enough to the decimal that the above-mentioned conversion will surely give you the same BigDecimal that you had before (unless you have really huge numbers like 10 digits before the decimal point - the you're lost with double anyway).
P.S. Be sure to use BigDecimal only if decimal fractions are relevant to you - there were times when the British Shilling currency consisted of twelve Pence. Representing fractional Pounds as BigDecimal would give a disaster much worse than using doubles.
It depends on the Database you are using. If you are using SQL Server you can use data type as numeric(12, 8) where 12 represent numeric value and 8 represents precision. similarly, for my SQL DECIMAL(5,2) you can use.
You won't lose any precision value if you use the above-mentioned datatype.
Java Hibernate Class :
You can define
private double latitude;
Database:

DecimalFormat: No leading zero, no exponential, variable precision

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).

Different rounding with println and printf

The first line below will print 0.8999999999999999 because of precision loss, this is clear. But the second line will print 0.9, I just do not understand why. Shouldn't there be the same problem with this calculation?
System.out.println(2.00-1.10);
System.out.printf("%f",2.00-1.10);
I think you are missing something as using System.out.printf(), if you do not explicit formatting widths then default behavior of printf in C (which is 6 decimal places if not explicitly specified)
So if you will not specify any number to %f then by default it will print only 1 character. However if you want to change the number after the decimal then you need to specify it like %.2f, this will print the number to 2 decimal places.
So it is similar to writing like
System.out.printf("%f",2.00-1.10);
or
System.out.printf("%.1f",2.00-1.10);
As the general syntax for format specifier for float is:
%[flags][width][.precision][argsize]typechar
On a side note:-
Also there is a formatter class in Java for this.
An interpreter for printf-style format strings. This class provides
support for layout justification and alignment, common formats for
numeric, string, and date/time data, and locale-specific output.
Common Java types such as byte, BigDecimal, and Calendar are
supported. Limited formatting customization for arbitrary user types
is provided through the Formattable interface.
From the Oracle Docs
If the precision is not specified then the default value is 6. If the
precision is less than the number of digits which would appear after
the decimal point in the string returned by Float.toString(float) or
Double.toString(double) respectively, then the value will be rounded
using the round half up algorithm.
This output is the due to reason of round half up operation of floating format %f. If you go through the docs, than you will get
If the precision is not specified then the default value is 6. If the
precision is less than the number of digits which would appear after
the decimal point in the string returned by Float.toString(float) or
Double.toString(double) respectively, then the value will be rounded
using the round half up algorithm.
according to me, the format %f prints 1 character.
the fact that it prints 0.9 is standard mathematical rounding behavior

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?

Categories

Resources