StreamTokenizer input number and parsed number are different - java

I have a StreamTokenizer that accepts numbers. However, the parsed number is not the same as the input.
Sample code:
String str = "1000000000000.0000000000000";
double initial = 1000000000000.0000000000000;
InputStream in = new ByteArrayInputStream(str.getBytes());
StreamTokenizer input = new StreamTokenizer(new BufferedReader(new InputStreamReader(in)));
input.parseNumbers();
int n = input.nextToken();
if (n == StreamTokenizer.TT_NUMBER) {
System.out.println("Original: " + str);
System.out.println("Parsed: " + input.nval);
System.out.println(initial + " == " + input.nval + " -> " + (initial == input.nval));
}
Output:
Original: 1000000000000.0000000000000
Parsed: 9.999999999999999E11
1.0E12 == 9.999999999999999E11 -> false
How can this be prevented so the two double values are equal?
EDIT: Linked question discusses why this issue appears. I am asking what are the possible ways to avoid this issue.

An exact way of representing numbers is usage of BigDecimal. Double has a certain precision and working with doubles of various precision (EX: double1=10000.0 and double2=0.0001) could result that the 0.0001 to be dropped. BigDecimal avoids that.
The disadvantages of BigDecimal:
it's slower
operators +, -, *, and / are not overloaded
But if you are dealing with money or having precision is a must, you should use BigDecimal, otherwise you will have losses.
EX:
String str = "1.0E12";
double initial = 1000000000000.0000000000000;
BigDecimal exVal = new BigDecimal(str);
System.out.println("Original: " + str);
System.out.println("Parsed: " + exVal);
System.out.println(initial + " == " + exVal + " -> " + (initial == exVal.doubleValue()));
Program output:
Original: 1.0E12
Parsed: 1.0E+12
1.0E12 == 1.0E+12 -> true

As per David Conrad's analysis, the StreamTokenizer classes handling of numbers with decimal points is flawed, and there doesn't appear to be a work-around.
But this is only one of many short-comings flaws in this class. You would be better off using Scanner or String.split. If the input is really complicated, consider using a parser generator to generate a lexer / parser that precisely implements your input syntax.
Related bugs:
JDK-4638205 : StreamTokenizer: lost precision when parsing long values
JDK-4146533 : java.io.StreamTokenizer: Want to parse scientific numbers
JDK-8180207 : deprecate StringTokenizer and StreamTokenizer
Having said that, applications that use floating point numbers should be tolerant of issues caused by rounding errors and imprecision. There are a number of Q&As on the best way to compare floating point numbers; e.g.
Manipulating and comparing floating points in java

Related

How to keep trailing 0s while converting a string to BigDecimal without exponent

I need to convert this two strings "0.0000000" and "0.0000008" to BigDecimal without losing trailing 0s.
I tried String.format
String format = "%." + precision + "f";
BigDecimal divisor = new BigDecimal(Math.pow(10, precision));
formattedAmount = amountValue.divide(divisor).setScale(precision, BigDecimal.ROUND_DOWN);
String str= String.format(format, formattedAmount);
System.out.println(str);
BigDecimal strb = new BigDecimal(str);
System.out.println(strb);
I am expecting these two strings "0.0000000" and "0.0000008" to be returned as BigDecimal without exponent like 0.0000000 and 0.0000008. But i am getting 0E-7 and 8E-7.
I need 0.0000000 and 0.0000008 as BigDecimal not as String.
When you're trying to print BigDecimal in System.out.println(strb) the toString() method on strb object will be invoked. So the output in console is also string.
See more The connection between 'System.out.println()' and 'toString()' in Java
So the only way to tell the java not to use exponent here System.out.println(strb) is to change toString() method. You can't change it directly, but you could create your own class that extends BigDecimal and override toString() method you need. Obviously it doesn't make sense.. just use toPlainString() method. From java doc:
return a string representation of this {#code BigDecimal} without an exponent field.
First of all, no matter how you output the value of a BigDecimal, it stays the same in an instance of it (as long as you don't do calculations on it).
However, the output may differ a lot.
Here are the methods BigDecimal is offering
public static void main(String[] args) {
String zero = "0.0000000";
String zeroDotsEight = "0.0000008";
BigDecimal z = new BigDecimal(zero);
BigDecimal zE = new BigDecimal(zeroDotsEight);
// play around with the precision and have a look at the output
int precision = 7;
System.out.println("String:\t\t\t\t\t" + zero);
System.out.println("BigDecimal:\t\t\t\t" + z.setScale(precision, BigDecimal.ROUND_DOWN));
System.out.println("BigDecimal.toString():\t\t\t"
+ z.setScale(precision, BigDecimal.ROUND_DOWN).toString());
System.out.println("BigDecimal.toPlainString():\t\t"
+ z.setScale(precision, BigDecimal.ROUND_DOWN).toPlainString());
System.out.println("BigDecimal.toEngineeringString():\t"
+ z.setScale(precision, BigDecimal.ROUND_DOWN).toEngineeringString());
System.out.println("BigDecimal.doubleValue():\t\t"
+ z.setScale(precision, BigDecimal.ROUND_DOWN).doubleValue());
System.out.println("————————————————————————————————————————————————————————————————");
System.out.println("String:\t\t\t\t\t" + zeroDotsEight);
System.out.println("BigDecimal:\t\t\t\t" + zE.setScale(precision, BigDecimal.ROUND_DOWN));
System.out.println("BigDecimal.toString():\t\t\t"
+ zE.setScale(precision, BigDecimal.ROUND_DOWN).toString());
System.out.println("BigDecimal.toPlainString():\t\t"
+ zE.setScale(precision, BigDecimal.ROUND_DOWN).toPlainString());
System.out.println("BigDecimal.toEngineeringString():\t"
+ zE.setScale(precision, BigDecimal.ROUND_DOWN).toEngineeringString());
System.out.println("BigDecimal.doubleValue():\t\t"
+ zE.setScale(precision, BigDecimal.ROUND_DOWN).doubleValue());
}
and this is the output on my system
String: 0.0000000
BigDecimal: 0E-7
BigDecimal.toString(): 0E-7
BigDecimal.toPlainString(): 0.0000000
BigDecimal.toEngineeringString(): 0.0E-6
BigDecimal.doubleValue(): 0.0
————————————————————————————————————————————————————————————————
String: 0.0000008
BigDecimal: 8E-7
BigDecimal.toString(): 8E-7
BigDecimal.toPlainString(): 0.0000008
BigDecimal.toEngineeringString(): 800E-9
BigDecimal.doubleValue(): 8.0E-7
As you can see, #Ruslan is right. You have to create your own class or use toPlainString() in order to keep the format and not lose any trailing zeros (which should only matter for the human eye, but I even doubt they really do).
In addition, you were right as well when you commented that doubleValue() is not the way to go.
As stated in the only code comment, play around with the precision, maybe calculate some values before printing and/or storing the results in variables.

How to round a large decimal value in Java?

I'm trying in the following code to round a decimal number based on the decimal format that I set at the beginning:
DecimalFormat df = new DecimalFormat("#.000");
df.setRoundingMode(RoundingMode.FLOOR);
double a = Double.parseDouble(df.format(43.473684210526315));
double b = Math.pow(a, 10);
double c = Double.parseDouble(df.format(b));
System.out.println(a + " ** " + b + " ** " + c);
The result that I got is:
43.473 ** 2.4109939006965688E16 ** 2.4109939006965688E16
As you see, the value of a is formatted properly. While the other values are not. After spending a long time trying to understand what is going on, I found out the value 2.4109939006965688E16 can not be formatted. I tested the same value after removing E16 and it worked.
My question is how can I round such a large decimal so that it works as the a?
It's pure luck that a works. If the initial value was 43.4701 you would be seeing 43.47. And in some cases you would see more than 3 decimal places due to the inaccuracy of double.
You only use the DecimalFormatter to create a String briefly before turning back into a double again. You want to keep and use that string.
DecimalFormat df = new DecimalFormat("#.000");
double b = Math.pow(43.473684210526315, 10);
String bFormatted = df.format(b);
System.out.println(bFormatted);
Gives you your desired output 24115485538109308.000
For your "big" decimals you can use the following format:
DecimalFormat df2 = new DecimalFormat("#.000E0");
I see several issues here:
You do not specify a locale for your DecimalFormat. So the combination of DecimalFormat.format and Double.parseDouble is error prone, as e.g. with german locale, DecimalFormat.format will use a comma as decimal separator, which is unsupported by Double.parseDouble, and will throw a NumberFormatException. You should use both format and parse of your DecimalFormat.
Also, you use your double values directly for System.out.println. Why not concatenating the results of DecimalFormat.format, to achieve exactly what you want? E.g. System.out.println(df.format(a) + " ** " + df.format(b) + " ** " + df.format(c));
If you want to do real mathematical rounding (e.g. needing the double value for additional calculations), use Math.round. You can multiply and divide by 10, 100, 1000 etc. to achieve the desired precision. Note that, with the double data type, you can't do exact rounding.
For large numbers, you'll need to use BigInteger or BigDecimal
DecimalFormat df = new DecimalFormat("#.000");
df.setRoundingMode(RoundingMode.FLOOR);
BigDecimal a = new BigDecimal(43.473);
BigDecimal b = a.pow(10);
System.out.println(df.format(a) + " ** " + b.doubleValue() + " ** " + df.format(b));
This results in the following output
43.472 ** 2.4109939006965688E16 ** 24109939006965686.655

How do I use "BigDecimal" in Java for my specific code?

I'm very new to programming in Java. I have been given an assignment in my school to solve the following exercise:
"Create two variables, each containing a number. Put out a message that shows how often the second number fits into the first one, and the rest (if there is one)" [I hope the wording is clear. I'm translating this from my native language german into english]
Now in general, I have solved the exercise like this (using Netbeans):
double numberOne = 10, numberTwo = 35.55;
double result, rest;
String conversion, numberOutput;
result = numberTwo / numberOne;
conversion = Double.toString(result);
int indexOfComma = conversion.indexOf(".");
numberOutput = conversion.substring(0, indexOfComma);
rest = numberTwo % numberOne;
System.out.println("The second number fits " + numberOutput +
" times into the first one. The rest is: " + rest);
With the numbers provided, the system pops out this message:
"The second number fits 3 times into the first one. The rest is: 5.549999999999997"
I don't like the rounding error for the rest. I expected it to give out "5.55" like a human would type or write it. After a bit of googling around it seems that something called "BigDecimal" is the solution to my problem, but the explanations I found of how to implement this in Java go wayyy over my head.
Would you be so kind as to show me exactly where and how I need to use BigDecimal in the above code to get the desired output? I would also be happy to see any alternative solutions you can think of.
BigDecimal version of your code:
BigDecimal numberOne = new BigDecimal("10");
BigDecimal numberTwo = new BigDecimal("35.55");
BigDecimal[] divRem = numberTwo.divideAndRemainder(numberOne);
System.out.println("The second number fits " + divRem[0].stripTrailingZeros().toPlainString() +
" times into the first one. The rest is: " + divRem[1].stripTrailingZeros().toPlainString());
Output
The second number fits 3 times into the first one. The rest is: 5.55
You can use BigDecimal like
BigDecimal a = BigDecimal.valueOf(10);
BigDecimal b = BigDecimal.valueOf(35.55);
BigDecimal c = b.divide(a, 3, BigDecimal.HALF_UP);
System.out.println(b + " / " + a + " = " + c);
Or you could use rounding like
System.out.printf("(int)(%.2f / %d) = %d%n", 35.55, 10, (int) (35.55 / 10));
System.out.printf("%.2f %% %d = %.2f%n", 35.55, 10, 35.55 % 10);
which prints
floor(35.55 / 10) = 3
35.55 % 10 = 5.55

Java: print double - println or printf

Suppose I have the following code:
double median = med(10.0, 12.0, 3.0); //method returns middle number as double
Now, I want to write a message stating the median. Can this be done using println(), like the following:
System.out.println("Your number was " + median);
Or must I use printf() with double values?
The reason I ask is because I have seen both practices online 1, but when I try using println() I get an error, unless i write:
System.out.println(median);
Hence, can you use println() to print out double values and strings, or must you use printf() for that purpose?
The printf method can be particularly useful when displaying multiple variables in one line which would be tedious using string concatenation:
The println() which can be confusing at times. (Although both can be used in almost all cases).
double a = 10;
double b = 20;
System.out.println("a: " + a + " b: " + b);// Tedious string concatenation.
System.out.printf("a: %f b: %f\n", a, b);// Output using string formatting.
Output:
a: 10.0 b: 20.0
a: 10,000000 b: 20,000000
You can do both (if System.out.println("Your number was " + median); is not working you have something else wrong. Show us the stack trace). printf allows you some more formatting options (such as the number of decimal places) without having to use a DecimalFormat, but it is a choice on style rather than anything else.
Personally I find string concatenation easer on the eyes and easer to maintain. The %blah gets lost in the string, errors in string concatenation is a compile time error and adding a new variable requires less thought. However I'm in the minority on this.
The pros in printf is that you don't need to construct a bunch of formatters, others find its format easer on the eyes and when using a printf like method provided by a logging framework the string building only happens if actually required.
double value = 0.5;
System.out.println("value: " + value);
System.out.printf("value: %f", value);
If you want to set a number of decimal places:
double value = 0.5;
// String concatenation
DecimalFormat df = new DecimalFormat("0.000");
System.out.println("value: " + df.format(value));
// printf
System.out.printf("value: %.3f", value);

Long datatype throwing numberformatexception

A datatype long in Java 7 is throwing numberformatexception when it is exceeding 20 digits. Is there any alternative here?
You can use BigIntegers to do mathematical operations with higher number of digits.
But in division, it is doing integer division.(As in integer data types). For large decimal number calculations, you can use BigDecimal
An Example using BigInteger is shown below..
BigInteger b1 = new BigInteger("100000000000000000000000000000000000000000");
BigInteger b2 = new BigInteger("100000");
System.out.println("Addition: " + b1.add(b2));
System.out.println("Subtraction: " + b1.subtract(b2));
System.out.println("Division: " + b1.divide(b2));
System.out.println("Multiplication: " + b1.multiply(b2));
System.out.println("Power: " + b1.pow(1000));
For more information you can just google how to use biginteger in java

Categories

Resources