Java Strange Behavior with Sin and ToRadians - java

I have been given the task of using java to produce a Sin table, however I seem to get some very weird results for some values of the input. I am using the below
System.out.println("| sin(" + currentPoint + ") = " + Math.sin(Math.toRadians(currentPoint)));
Where (int) currentPoint is a value in degrees (eg 90)
These are results I find weird
| sin(360) = -2.4492935982947064E-16
| sin(180) = 1.2246467991473532E-16
| sin(150) = 0.49999999999999994
| sin(120) = 0.8660254037844387
Expecting
sin(360) = 0
sin(180) = 0
sin(150) = 0.5
sin(120) = 0.866025404
Am I missing something?

You're dealing with floating point numbers, looking for exact answers isn't going to work for all values. Take a look at
What Every Computer Scientist Should Know About Floating-Point Arithmetic. You want your tests to be equivalent to your expectations within some delta. Note that the answers you're getting are pretty close. It's expressing values in bits that's biting you.
From the link:
Squeezing infinitely many real numbers into a finite number of bits requires an approximate representation. Although there are infinitely many integers, in most programs the result of integer computations can be stored in 32 bits. In contrast, given any fixed number of bits, most calculations with real numbers will produce quantities that cannot be exactly represented using that many bits. Therefore the result of a floating-point calculation must often be rounded in order to fit back into its finite representation. This rounding error is the characteristic feature of floating-point computation.

If your code was System.out.println("| sin(" + currentPoint + ") = " + Math.sin(currentPoint)); you would expect this:
sin(360) = 0.958915723
sin(180) = -0.801152636
sin(150) = -0.71487643
sin(120) = 0.580611184
In other words, the sine of 360 radians is 0.9589, but the sine of 360 degrees is 0.
EDIT:
The reason you're seeing unexpected results is just due to lack of precision in the calculations. If you just format the results so they have fewer decimal places, the rounding will take care of it. Do something like this:
System.out.printf("| sin(%d) = %.7f\n", currentPoint, Math.sin(Math.toRadians(currentPoint)));
Then you will get results closer to what you expect.

Your results are correct ... for approximation try this...
result=Math.sin(Math.toRadians(value));
result=format(result);
private double format(double value) {
return (double)Math.round(value * 1000000) / 1000000; //you can change this to round up the value(for two position use 100...)
}

As mentioned above it is not an error, just the aproximation of computer's floating point arithmetic.
To get the expected answer, as sin() & cos() are between -1, 0 , +1, try to add 1 round it to the accurancy needed and substract 1.
x = round15(Math.sin(toRad(angle))+1)-1;
where round15 is defined
public double round15(double x){
DecimalFormat twoDForm = new DecimalFormat("0.##############E0");
String str = twoDForm.format(x);
return Double.valueOf(str);
}
It works for me, hope future readers like it.

The posters above are right. The correct values you are expecting are:
Sin(360 degrees) = 0
Sin(180 degrees) = 0
Sin(150 degrees) = .5
Sin(120 degrees) = .866
The code is returning the correct answers. They just need to be rounded. Try this:
System.out.printf("%s%.3f","| sin(" + currentPoint + ") = ", (Math.sin(Math.toRadians(currentPoint))));
You can change the .3f value to different numbers if you want to improve or reduce decimal precision.
For some reason it displays the sin of 360 to be -0.00. I am sure there is a more elegant solution, but this should work.
EDIT: Beaten by seconds. Use the code above mine, it is easier to read.

Also note that Math.PI, which is a double value, is not PI, but just an approximation of PI, and Math.sin(Math.PI) gives you the double value which is the closest to actual mathematical value of sin(Math.PI).

Below is the API description of the Math.sin method. Note the part in asterix.
I would bet that the difference between your expected results and the once you get are defects of floatpoint calculation or rounding problems.
sin
public static double sin(double a)
Returns the trigonometric sine of an angle. Special cases:
* If the argument is NaN or an infinity, then the result is NaN.
* If the argument is zero, then the result is a zero with the
same sign as the argument.
A result must be within 1 **ulp** of the correctly rounded result. Results
must be semi-monotonic.
Parameters:
a - an angle, in radians.
Returns:
the sine of the argument.

You must convert the angle into radians like this
Math.sin(Math.toRadians(90)) and the result must be 1

Related

Observations with Round-ing in Android Studio - java. And some practical explanations expected

In Android Studio I had problems with calculating invoice totals because of the way java rounds. I know there are a lot of explanations, but many recommend methods that don't return reliable results.
For example:
1. Math.round((double)4.715 * (double)100 ) / (double)100 = 4.72 (expected 4.72)
2. Math.round((double)4.725 * (double)100 ) / (double)100 = 4.72 (but expected 4.73)
You can't put this code in an app for a client who calculates invoices. Because , in my case for example, the same invoice is calculated in another system and the result is different, meaning 4.72 respectively 4.73
I know that a double can't be represented exactly and the decimals are different than what we see. But we need a method that returns results as we expect.
Another example would be:
1. java.math.BigDecimal.valueOf(4.715).setScale(2,java.math.BigDecimal.ROUND_HALF_UP).doubleValue() = 4.72
2. new java.math.BigDecimal(4.715).setScale(2,java.math.BigDecimal.ROUND_HALF_UP).doubleValue() = 4.71
3. new java.math.BigDecimal( String.valueOf(4.715) ).setScale(2,java.math.BigDecimal.ROUND_HALF_UP).doubleValue() = 4.72
I think all these aspects could be well explained in Java documentation, but they should indicate a certain method for calculating rounds, a reliable method which returns results as we expected. I only wanted to round to 2 decimales.
In conclusion, which I hope will help some of the beginners, I think that the following method would return stable and good results:
java.math.BigDecimal.valueOf(4.715).setScale(2,java.math.BigDecimal.ROUND_HALF_UP).doubleValue() = 4.72
Or, at least, this is my observation after 3+ years of intensive usage of an app (500+ users every working day).
All practical explanations for these above are very welcome, so we can better understand how to avoid unexpected results.
For the BigDecimal examples the javadoc explains the difference.
BigDecimal(double value) ... is the exact decimal representation of the double's binary floating-point value.
Which we can check, by just printing the value.
System.out.println(new BigDecimal(4.715));
#4.714999999999999857891452847979962825775146484375
Which is barely less than 4.715, but enough such that it gets rounded down.
BigDecimal.valueOf(double value) uses the string representation of the double value from Double.toString(double value) which has quite a few rules.
System.out.println(Double.toString(4.715));
#4.715
The safest best is to just use BigDecimal for your calculations. Especially when dealing with arithmetic operations. It isn't clear when the value will switch to needing more decimal places. For example:
double d = 4.11547;
BigDecimal bd = BigDecimal.valueOf(d);
I this case, the string representation of d is 4.11547, so BigDecimal.valueOf returns the value that is written.
BigDecimal s1 = BigDecimal.valueOf(d-3);
BigDecimal s2 = bd.subtract(new BigDecimal(3));
It might be surprising to find s1 and s2 are different since '3' doesn't get rounded.
System.out.println(s1 + ", " + s2);
#1.1154700000000002, 1.11547
So it is best to use the BigDecimal methods for arithmetic too.
It's in the nature of binary floating point data types, like float and double in Java. double actually states this in his name. It has double precision compared to float - but it is not an exact representation of a decimal number.
Just adding some simplified math detail to the existing answer. This might help understand the seemingly strange behavior of Java floating point numbers.
The root cause of the problem is binary vs. decimal representation of numbers. You use decimal representation when you use a floating point literal in your code, e.g. double d = 1.5; or a String value, e.g. String s = "1.5";.
But the JVM uses a binary representation of the number. The mapping for integer numbers is easy (d for decimal, b for binary): 1 = 1b, 2d = 10b, 3d = 11b .... There is no issue with integer numbers. int and long work just the way you would expect. Except for the overflow...
But for floating point numbers things are different: 0.5d = 0.1b, 0.25d = 0.01b, 0.125d = 0.001b.... You are only able to add values for the series 1/2, 1/4, 1/8, 1/16... Now imagine, you want to show 0.1d in binary representation.
You start with 0.0001b = 0.0625d, which is the first binary value that is still less than 0.1d. 0.0375d remaining. You continue, and the next close value is 0.03125d, and so on. You'll acutally never get to exactly 0.1d. All you get is an approximation. You'll get closer and closer.
Consider the following piece of code. It does the approximation with the help of BigDecimal values:
public void approximate0dot1() {
BigDecimal destVal = new BigDecimal("0.1");
BigDecimal curVal = new BigDecimal("0");
BigDecimal inc = new BigDecimal("1");
BigDecimal div = new BigDecimal("2");
for (int step = 0; step < 20; step++) {
BigDecimal probeVal = curVal.add(inc);
int cmp = probeVal.compareTo(destVal);
if (cmp == 0) {
break;
} else if (cmp < 0) {
curVal = probeVal;
System.out.format("Added: %s, current value: %s, remaining: %s\n", inc, curVal, destVal.subtract(curVal));
}
inc = inc.divide(div);
}
System.out.format("Final value: %s\n", curVal);
}
And the output is:
Added: 0.0625, current value: 0.0625, remaining: 0.0375
Added: 0.03125, current value: 0.09375, remaining: 0.00625
Added: 0.00390625, current value: 0.09765625, remaining: 0.00234375
Added: 0.001953125, current value: 0.099609375, remaining: 0.000390625
Added: 0.000244140625, current value: 0.099853515625, remaining: 0.000146484375
Added: 0.0001220703125, current value: 0.0999755859375, remaining: 0.0000244140625
Added: 0.0000152587890625, current value: 0.0999908447265625, remaining: 0.0000091552734375
Added: 0.00000762939453125, current value: 0.09999847412109375, remaining: 0.00000152587890625
Final value: 0.09999847412109375
This is just a basic example to show the underlying issue. Internally, the JVM obviously does some optimization to get the best possible approximation for the available 64-bit precision, e.g.
System.out.println(new BigDecimal(0.1));
// prints 0.1000000000000000055511151231257827021181583404541015625
But this example shows, that there is already a rounding issue with decimal numbers a simple as a constant with the decimal value 0.1.
Some basic tips:
Do not use BigDecimal(double) constructor if you need exact decimal math, use BigDecimal(String) instead. Bad: new BigDecimal(0.1), Good: new BigDecimal("0.1")
Do not mix BigDecimal and floating point arithmetic, e.g. do not extract double value for further calculations like new BigDecimal("0.1").doubleValue();

double inaccuracy [duplicate]

public class doublePrecision {
public static void main(String[] args) {
double total = 0;
total += 5.6;
total += 5.8;
System.out.println(total);
}
}
The above code prints:
11.399999999999
How would I get this to just print (or be able to use it as) 11.4?
As others have mentioned, you'll probably want to use the BigDecimal class, if you want to have an exact representation of 11.4.
Now, a little explanation into why this is happening:
The float and double primitive types in Java are floating point numbers, where the number is stored as a binary representation of a fraction and a exponent.
More specifically, a double-precision floating point value such as the double type is a 64-bit value, where:
1 bit denotes the sign (positive or negative).
11 bits for the exponent.
52 bits for the significant digits (the fractional part as a binary).
These parts are combined to produce a double representation of a value.
(Source: Wikipedia: Double precision)
For a detailed description of how floating point values are handled in Java, see the Section 4.2.3: Floating-Point Types, Formats, and Values of the Java Language Specification.
The byte, char, int, long types are fixed-point numbers, which are exact representions of numbers. Unlike fixed point numbers, floating point numbers will some times (safe to assume "most of the time") not be able to return an exact representation of a number. This is the reason why you end up with 11.399999999999 as the result of 5.6 + 5.8.
When requiring a value that is exact, such as 1.5 or 150.1005, you'll want to use one of the fixed-point types, which will be able to represent the number exactly.
As has been mentioned several times already, Java has a BigDecimal class which will handle very large numbers and very small numbers.
From the Java API Reference for the BigDecimal class:
Immutable,
arbitrary-precision signed decimal
numbers. A BigDecimal consists of an
arbitrary precision integer unscaled
value and a 32-bit integer scale. If
zero or positive, the scale is the
number of digits to the right of the
decimal point. If negative, the
unscaled value of the number is
multiplied by ten to the power of the
negation of the scale. The value of
the number represented by the
BigDecimal is therefore (unscaledValue
× 10^-scale).
There has been many questions on Stack Overflow relating to the matter of floating point numbers and its precision. Here is a list of related questions that may be of interest:
Why do I see a double variable initialized to some value like 21.4 as 21.399999618530273?
How to print really big numbers in C++
How is floating point stored? When does it matter?
Use Float or Decimal for Accounting Application Dollar Amount?
If you really want to get down to the nitty gritty details of floating point numbers, take a look at What Every Computer Scientist Should Know About Floating-Point Arithmetic.
When you input a double number, for example, 33.33333333333333, the value you get is actually the closest representable double-precision value, which is exactly:
33.3333333333333285963817615993320941925048828125
Dividing that by 100 gives:
0.333333333333333285963817615993320941925048828125
which also isn't representable as a double-precision number, so again it is rounded to the nearest representable value, which is exactly:
0.3333333333333332593184650249895639717578887939453125
When you print this value out, it gets rounded yet again to 17 decimal digits, giving:
0.33333333333333326
If you just want to process values as fractions, you can create a Fraction class which holds a numerator and denominator field.
Write methods for add, subtract, multiply and divide as well as a toDouble method. This way you can avoid floats during calculations.
EDIT: Quick implementation,
public class Fraction {
private int numerator;
private int denominator;
public Fraction(int n, int d){
numerator = n;
denominator = d;
}
public double toDouble(){
return ((double)numerator)/((double)denominator);
}
public static Fraction add(Fraction a, Fraction b){
if(a.denominator != b.denominator){
double aTop = b.denominator * a.numerator;
double bTop = a.denominator * b.numerator;
return new Fraction(aTop + bTop, a.denominator * b.denominator);
}
else{
return new Fraction(a.numerator + b.numerator, a.denominator);
}
}
public static Fraction divide(Fraction a, Fraction b){
return new Fraction(a.numerator * b.denominator, a.denominator * b.numerator);
}
public static Fraction multiply(Fraction a, Fraction b){
return new Fraction(a.numerator * b.numerator, a.denominator * b.denominator);
}
public static Fraction subtract(Fraction a, Fraction b){
if(a.denominator != b.denominator){
double aTop = b.denominator * a.numerator;
double bTop = a.denominator * b.numerator;
return new Fraction(aTop-bTop, a.denominator*b.denominator);
}
else{
return new Fraction(a.numerator - b.numerator, a.denominator);
}
}
}
Observe that you'd have the same problem if you used limited-precision decimal arithmetic, and wanted to deal with 1/3: 0.333333333 * 3 is 0.999999999, not 1.00000000.
Unfortunately, 5.6, 5.8 and 11.4 just aren't round numbers in binary, because they involve fifths. So the float representation of them isn't exact, just as 0.3333 isn't exactly 1/3.
If all the numbers you use are non-recurring decimals, and you want exact results, use BigDecimal. Or as others have said, if your values are like money in the sense that they're all a multiple of 0.01, or 0.001, or something, then multiply everything by a fixed power of 10 and use int or long (addition and subtraction are trivial: watch out for multiplication).
However, if you are happy with binary for the calculation, but you just want to print things out in a slightly friendlier format, try java.util.Formatter or String.format. In the format string specify a precision less than the full precision of a double. To 10 significant figures, say, 11.399999999999 is 11.4, so the result will be almost as accurate and more human-readable in cases where the binary result is very close to a value requiring only a few decimal places.
The precision to specify depends a bit on how much maths you've done with your numbers - in general the more you do, the more error will accumulate, but some algorithms accumulate it much faster than others (they're called "unstable" as opposed to "stable" with respect to rounding errors). If all you're doing is adding a few values, then I'd guess that dropping just one decimal place of precision will sort things out. Experiment.
You may want to look into using java's java.math.BigDecimal class if you really need precision math. Here is a good article from Oracle/Sun on the case for BigDecimal. While you can never represent 1/3 as someone mentioned, you can have the power to decide exactly how precise you want the result to be. setScale() is your friend.. :)
Ok, because I have way too much time on my hands at the moment here is a code example that relates to your question:
import java.math.BigDecimal;
/**
* Created by a wonderful programmer known as:
* Vincent Stoessel
* xaymaca#gmail.com
* on Mar 17, 2010 at 11:05:16 PM
*/
public class BigUp {
public static void main(String[] args) {
BigDecimal first, second, result ;
first = new BigDecimal("33.33333333333333") ;
second = new BigDecimal("100") ;
result = first.divide(second);
System.out.println("result is " + result);
//will print : result is 0.3333333333333333
}
}
and to plug my new favorite language, Groovy, here is a neater example of the same thing:
import java.math.BigDecimal
def first = new BigDecimal("33.33333333333333")
def second = new BigDecimal("100")
println "result is " + first/second // will print: result is 0.33333333333333
Pretty sure you could've made that into a three line example. :)
If you want exact precision, use BigDecimal. Otherwise, you can use ints multiplied by 10 ^ whatever precision you want.
As others have noted, not all decimal values can be represented as binary since decimal is based on powers of 10 and binary is based on powers of two.
If precision matters, use BigDecimal, but if you just want friendly output:
System.out.printf("%.2f\n", total);
Will give you:
11.40
You're running up against the precision limitation of type double.
Java.Math has some arbitrary-precision arithmetic facilities.
You can't, because 7.3 doesn't have a finite representation in binary. The closest you can get is 2054767329987789/2**48 = 7.3+1/1407374883553280.
Take a look at http://docs.python.org/tutorial/floatingpoint.html for a further explanation. (It's on the Python website, but Java and C++ have the same "problem".)
The solution depends on what exactly your problem is:
If it's that you just don't like seeing all those noise digits, then fix your string formatting. Don't display more than 15 significant digits (or 7 for float).
If it's that the inexactness of your numbers is breaking things like "if" statements, then you should write if (abs(x - 7.3) < TOLERANCE) instead of if (x == 7.3).
If you're working with money, then what you probably really want is decimal fixed point. Store an integer number of cents or whatever the smallest unit of your currency is.
(VERY UNLIKELY) If you need more than 53 significant bits (15-16 significant digits) of precision, then use a high-precision floating-point type, like BigDecimal.
private void getRound() {
// this is very simple and interesting
double a = 5, b = 3, c;
c = a / b;
System.out.println(" round val is " + c);
// round val is : 1.6666666666666667
// if you want to only two precision point with double we
// can use formate option in String
// which takes 2 parameters one is formte specifier which
// shows dicimal places another double value
String s = String.format("%.2f", c);
double val = Double.parseDouble(s);
System.out.println(" val is :" + val);
// now out put will be : val is :1.67
}
Use java.math.BigDecimal
Doubles are binary fractions internally, so they sometimes cannot represent decimal fractions to the exact decimal.
/*
0.8 1.2
0.7 1.3
0.7000000000000002 2.3
0.7999999999999998 4.2
*/
double adjust = fToInt + 1.0 - orgV;
// The following two lines works for me.
String s = String.format("%.2f", adjust);
double val = Double.parseDouble(s);
System.out.println(val); // output: 0.8, 0.7, 0.7, 0.8
Doubles are approximations of the decimal numbers in your Java source. You're seeing the consequence of the mismatch between the double (which is a binary-coded value) and your source (which is decimal-coded).
Java's producing the closest binary approximation. You can use the java.text.DecimalFormat to display a better-looking decimal value.
Short answer: Always use BigDecimal and make sure you are using the constructor with String argument, not the double one.
Back to your example, the following code will print 11.4, as you wish.
public class doublePrecision {
public static void main(String[] args) {
BigDecimal total = new BigDecimal("0");
total = total.add(new BigDecimal("5.6"));
total = total.add(new BigDecimal("5.8"));
System.out.println(total);
}
}
Multiply everything by 100 and store it in a long as cents.
Computers store numbers in binary and can't actually represent numbers such as 33.333333333 or 100.0 exactly. This is one of the tricky things about using doubles. You will have to just round the answer before showing it to a user. Luckily in most applications, you don't need that many decimal places anyhow.
Floating point numbers differ from real numbers in that for any given floating point number there is a next higher floating point number. Same as integers. There's no integer between 1 and 2.
There's no way to represent 1/3 as a float. There's a float below it and there's a float above it, and there's a certain distance between them. And 1/3 is in that space.
Apfloat for Java claims to work with arbitrary precision floating point numbers, but I've never used it. Probably worth a look.
http://www.apfloat.org/apfloat_java/
A similar question was asked here before
Java floating point high precision library
Use a BigDecimal. It even lets you specify rounding rules (like ROUND_HALF_EVEN, which will minimize statistical error by rounding to the even neighbor if both are the same distance; i.e. both 1.5 and 2.5 round to 2).
Why not use the round() method from Math class?
// The number of 0s determines how many digits you want after the floating point
// (here one digit)
total = (double)Math.round(total * 10) / 10;
System.out.println(total); // prints 11.4
Check out BigDecimal, it handles problems dealing with floating point arithmetic like that.
The new call would look like this:
term[number].coefficient.add(co);
Use setScale() to set the number of decimal place precision to be used.
If you have no choice other than using double values, can use the below code.
public static double sumDouble(double value1, double value2) {
double sum = 0.0;
String value1Str = Double.toString(value1);
int decimalIndex = value1Str.indexOf(".");
int value1Precision = 0;
if (decimalIndex != -1) {
value1Precision = (value1Str.length() - 1) - decimalIndex;
}
String value2Str = Double.toString(value2);
decimalIndex = value2Str.indexOf(".");
int value2Precision = 0;
if (decimalIndex != -1) {
value2Precision = (value2Str.length() - 1) - decimalIndex;
}
int maxPrecision = value1Precision > value2Precision ? value1Precision : value2Precision;
sum = value1 + value2;
String s = String.format("%." + maxPrecision + "f", sum);
sum = Double.parseDouble(s);
return sum;
}
You can Do the Following!
System.out.println(String.format("%.12f", total));
if you change the decimal value here %.12f
So far I understand it as main goal to get correct double from wrong double.
Look for my solution how to get correct value from "approximate" wrong value - if it is real floating point it rounds last digit - counted from all digits - counting before dot and try to keep max possible digits after dot - hope that it is enough precision for most cases:
public static double roundError(double value) {
BigDecimal valueBigDecimal = new BigDecimal(Double.toString(value));
String valueString = valueBigDecimal.toPlainString();
if (!valueString.contains(".")) return value;
String[] valueArray = valueString.split("[.]");
int places = 16;
places -= valueArray[0].length();
if ("56789".contains("" + valueArray[0].charAt(valueArray[0].length() - 1))) places--;
//System.out.println("Rounding " + value + "(" + valueString + ") to " + places + " places");
return valueBigDecimal.setScale(places, RoundingMode.HALF_UP).doubleValue();
}
I know it is long code, sure not best, maybe someone can fix it to be more elegant. Anyway it is working, see examples:
roundError(5.6+5.8) = 11.399999999999999 = 11.4
roundError(0.4-0.3) = 0.10000000000000003 = 0.1
roundError(37235.137567000005) = 37235.137567
roundError(1/3) 0.3333333333333333 = 0.333333333333333
roundError(3723513756.7000005) = 3.7235137567E9 (3723513756.7)
roundError(3723513756123.7000005) = 3.7235137561237E12 (3723513756123.7)
roundError(372351375612.7000005) = 3.723513756127E11 (372351375612.7)
roundError(1.7976931348623157) = 1.797693134862316
Do not waste your efford using BigDecimal. In 99.99999% cases you don't need it. java double type is of cource approximate but in almost all cases, it is sufficiently precise. Mind that your have an error at 14th significant digit. This is really negligible!
To get nice output use:
System.out.printf("%.2f\n", total);

Why does changing the sum order returns a different result?

Why does changing the sum order returns a different result?
23.53 + 5.88 + 17.64 = 47.05
23.53 + 17.64 + 5.88 = 47.050000000000004
Both Java and JavaScript return the same results.
I understand that, due to the way floating point numbers are represented in binary, some rational numbers (like 1/3 - 0.333333...) cannot be represented precisely.
Why does simply changing the order of the elements affect the result?
Maybe this question is stupid, but why does simply changing the order of the elements affects the result?
It will change the points at which the values are rounded, based on their magnitude. As an example of the kind of thing that we're seeing, let's pretend that instead of binary floating point, we were using a decimal floating point type with 4 significant digits, where each addition is performed at "infinite" precision and then rounded to the nearest representable number. Here are two sums:
1/3 + 2/3 + 2/3 = (0.3333 + 0.6667) + 0.6667
= 1.000 + 0.6667 (no rounding needed!)
= 1.667 (where 1.6667 is rounded to 1.667)
2/3 + 2/3 + 1/3 = (0.6667 + 0.6667) + 0.3333
= 1.333 + 0.3333 (where 1.3334 is rounded to 1.333)
= 1.666 (where 1.6663 is rounded to 1.666)
We don't even need non-integers for this to be a problem:
10000 + 1 - 10000 = (10000 + 1) - 10000
= 10000 - 10000 (where 10001 is rounded to 10000)
= 0
10000 - 10000 + 1 = (10000 - 10000) + 1
= 0 + 1
= 1
This demonstrates possibly more clearly that the important part is that we have a limited number of significant digits - not a limited number of decimal places. If we could always keep the same number of decimal places, then with addition and subtraction at least, we'd be fine (so long as the values didn't overflow). The problem is that when you get to bigger numbers, smaller information is lost - the 10001 being rounded to 10000 in this case. (This is an example of the problem that Eric Lippert noted in his answer.)
It's important to note that the values on the first line of the right hand side are the same in all cases - so although it's important to understand that your decimal numbers (23.53, 5.88, 17.64) won't be represented exactly as double values, that's only a problem because of the problems shown above.
Here's what's going on in binary. As we know, some floating-point values cannot be represented exactly in binary, even if they can be represented exactly in decimal. These 3 numbers are just examples of that fact.
With this program I output the hexadecimal representations of each number and the results of each addition.
public class Main{
public static void main(String args[]) {
double x = 23.53; // Inexact representation
double y = 5.88; // Inexact representation
double z = 17.64; // Inexact representation
double s = 47.05; // What math tells us the sum should be; still inexact
printValueAndInHex(x);
printValueAndInHex(y);
printValueAndInHex(z);
printValueAndInHex(s);
System.out.println("--------");
double t1 = x + y;
printValueAndInHex(t1);
t1 = t1 + z;
printValueAndInHex(t1);
System.out.println("--------");
double t2 = x + z;
printValueAndInHex(t2);
t2 = t2 + y;
printValueAndInHex(t2);
}
private static void printValueAndInHex(double d)
{
System.out.println(Long.toHexString(Double.doubleToLongBits(d)) + ": " + d);
}
}
The printValueAndInHex method is just a hex-printer helper.
The output is as follows:
403787ae147ae148: 23.53
4017851eb851eb85: 5.88
4031a3d70a3d70a4: 17.64
4047866666666666: 47.05
--------
403d68f5c28f5c29: 29.41
4047866666666666: 47.05
--------
404495c28f5c28f6: 41.17
4047866666666667: 47.050000000000004
The first 4 numbers are x, y, z, and s's hexadecimal representations. In IEEE floating point representation, bits 2-12 represent the binary exponent, that is, the scale of the number. (The first bit is the sign bit, and the remaining bits for the mantissa.) The exponent represented is actually the binary number minus 1023.
The exponents for the first 4 numbers are extracted:
sign|exponent
403 => 0|100 0000 0011| => 1027 - 1023 = 4
401 => 0|100 0000 0001| => 1025 - 1023 = 2
403 => 0|100 0000 0011| => 1027 - 1023 = 4
404 => 0|100 0000 0100| => 1028 - 1023 = 5
First set of additions
The second number (y) is of smaller magnitude. When adding these two numbers to get x + y, the last 2 bits of the second number (01) are shifted out of range and do not figure into the calculation.
The second addition adds x + y and z and adds two numbers of the same scale.
Second set of additions
Here, x + z occurs first. They are of the same scale, but they yield a number that is higher up in scale:
404 => 0|100 0000 0100| => 1028 - 1023 = 5
The second addition adds x + z and y, and now 3 bits are dropped from y to add the numbers (101). Here, there must be a round upwards, because the result is the next floating point number up: 4047866666666666 for the first set of additions vs. 4047866666666667 for the second set of additions. That error is significant enough to show in the printout of the total.
In conclusion, be careful when performing mathematical operations on IEEE numbers. Some representations are inexact, and they become even more inexact when the scales are different. Add and subtract numbers of similar scale if you can.
Jon's answer is of course correct. In your case the error is no larger than the error you would accumulate doing any simple floating point operation. You've got a scenario where in one case you get zero error and in another you get a tiny error; that's not actually that interesting a scenario. A good question is: are there scenarios where changing the order of calculations goes from a tiny error to a (relatively) enormous error? The answer is unambiguously yes.
Consider for example:
x1 = (a - b) + (c - d) + (e - f) + (g - h);
vs
x2 = (a + c + e + g) - (b + d + f + h);
vs
x3 = a - b + c - d + e - f + g - h;
Obviously in exact arithmetic they would be the same. It is entertaining to try to find values for a, b, c, d, e, f, g, h such that the values of x1 and x2 and x3 differ by a large quantity. See if you can do so!
This actually covers much more than just Java and Javascript, and would likely affect any programming language using floats or doubles.
In memory, floating points use a special format along the lines of IEEE 754 (the converter provides much better explanation than I can).
Anyways, here's the float converter.
http://www.h-schmidt.net/FloatConverter/
The thing about the order of operations is the "fineness" of the operation.
Your first line yields 29.41 from the first two values, which gives us 2^4 as the exponent.
Your second line yields 41.17 which gives us 2^5 as the exponent.
We're losing a significant figure by increasing the exponent, which is likely to change the outcome.
Try ticking the last bit on the far right on and off for 41.17 and you can see that something as "insignificant" as 1/2^23 of the exponent would be enough to cause this floating point difference.
Edit: For those of you who remember significant figures, this would fall under that category. 10^4 + 4999 with a significant figure of 1 is going to be 10^4. In this case, the significant figure is much smaller, but we can see the results with the .00000000004 attached to it.
Floating point numbers are represented using the IEEE 754 format, which provides a specific size of bits for the mantissa (significand). Unfortunately this gives you a specific number of 'fractional building blocks' to play with, and certain fractional values cannot be represented precisely.
What is happening in your case is that in the second case, the addition is probably running into some precision issue because of the order the additions are evaluated. I haven't calculated the values, but it could be for example that 23.53 + 17.64 cannot be precisely represented, while 23.53 + 5.88 can.
Unfortunately it is a known problem that you just have to deal with.
I believe it has to do with the order of evaulation. While the sum is naturally the same in a math world, in the binary world instead of A + B + C = D, it's
A + B = E
E + C = D(1)
So there's that secondary step where floating point numbers can get off.
When you change the order,
A + C = F
F + B = D(2)
To add a different angle to the other answers here, this SO answer shows that there are ways of doing floating-point math where all summation orders return exactly the same value at the bit level.

Java, BigDecimal. Problems with division

I'm trying to calculate a percentage "factor". That is, given a 20%, convert it into 0.2 (my intention is to later multiply values by that and get the 20% of the values).
Anyway, the question is related with this piece of code:
public static void main(String[] args) {
int roundingMode = BigDecimal.ROUND_FLOOR;
BigDecimal hundred = new BigDecimal("100");
BigDecimal percentageFactor = null;
BigDecimal percentage = new BigDecimal("20");
BigDecimal value = new BigDecimal("500");
percentageFactor = percentage.divide(hundred, roundingMode);
float f = percentage.floatValue() / hundred.floatValue();
f = value.floatValue() * f;
BigDecimal aux = value.multiply(percentageFactor);
System.out.println("factor:"+percentageFactor.toString());
System.out.println("final falue:"+aux.toString());
System.out.println("Float Value:"+f);
}
I would expect the outcome of this to be something like:
factor: 0.2
final value: 100
float value: 100
but instead percentage.divide(hundred, roundingMode); is returning zero, an hence I get:
factor:0
final falue:0
Float Value:100.0
What am I doing wrong? How can I divide two big decimals properly?
By the way, I'm using BigDecimal because I will be calculating monetary percentages, so I want control regarding rounding.
I think that the best solution is to set the requested scale when dividing: In this case perhaps 2.
var hundred = new BigDecimal(100);
var percentage = new BigDecimal(20);
var value = new BigDecimal(500);
var percentageFactor =
percentage.divide(hundred,2, BigDecimal.ROUND_HALF_UP);
value = value.multiply(percentageFactor);
System.out.println("final value:"+ value);
Final value: 100.00
The multiplication is using the scale from the factors (0+2) but it can be specified too.
I'd use ROUND_HALF_UP for accounting (in my legislation) or ROUND_EVEN (for statistics) for rounding mode.
The scale of new BigDecimal("20") is zero because you've got no decimal point in there. That means that your percentage.divide(hundred, BigDecimal.ROUND_FLOOR) will produce zero (it's effectively int(20/100) or 0).
If you really want to do fractional stuff, use new BigDecimal("20.00") so the scale is set correctly, or use one of the other constructors to set the scale specifically.
Here's the output from that simple change of 20 to 20.00, complete with your spellink misteak :-)
factor:0.20
final falue:100.00
Float Value:100.0
float has only 6 digits of accuracy and is almost never a good choice, I would suggest you use double instead. (or BigDecimal can be better in some cases)
The reason factor is 0 instead of 0.2 in your code is because
you've set the RoundingMode to be FLOOR (which means ROUND DOWN), and
your percentage variable has an implicit scale of 0 (any BigDecimals initialised from round number without specifying scale will have scale of 0)
So when you call divide you are rounding down any decimals and you are maintaining a scale of 0, and hence 0.2 is rounded down to 0.
To get the correct number, you can either
specify the scale explicitly, or
since you know you are dividing against 100, you can just use the BigDecimal#divide(BigDecimal) method instead (without providing scale or RoundingMethod). In your case, the method will not throw ArithmeticException since there is no possibility of non-terminating decimals (think 20 / 100 = 0.2, 20 / 10000 = 0.002 - decimals always terminate when dividing by 100).
However, if you're dividing against another number say 3 then you need to specify the scale because there is a possibility of non-terminating decimals (think 10 / 3 = 3.3333333333...)

Retain precision with double in Java

public class doublePrecision {
public static void main(String[] args) {
double total = 0;
total += 5.6;
total += 5.8;
System.out.println(total);
}
}
The above code prints:
11.399999999999
How would I get this to just print (or be able to use it as) 11.4?
As others have mentioned, you'll probably want to use the BigDecimal class, if you want to have an exact representation of 11.4.
Now, a little explanation into why this is happening:
The float and double primitive types in Java are floating point numbers, where the number is stored as a binary representation of a fraction and a exponent.
More specifically, a double-precision floating point value such as the double type is a 64-bit value, where:
1 bit denotes the sign (positive or negative).
11 bits for the exponent.
52 bits for the significant digits (the fractional part as a binary).
These parts are combined to produce a double representation of a value.
(Source: Wikipedia: Double precision)
For a detailed description of how floating point values are handled in Java, see the Section 4.2.3: Floating-Point Types, Formats, and Values of the Java Language Specification.
The byte, char, int, long types are fixed-point numbers, which are exact representions of numbers. Unlike fixed point numbers, floating point numbers will some times (safe to assume "most of the time") not be able to return an exact representation of a number. This is the reason why you end up with 11.399999999999 as the result of 5.6 + 5.8.
When requiring a value that is exact, such as 1.5 or 150.1005, you'll want to use one of the fixed-point types, which will be able to represent the number exactly.
As has been mentioned several times already, Java has a BigDecimal class which will handle very large numbers and very small numbers.
From the Java API Reference for the BigDecimal class:
Immutable,
arbitrary-precision signed decimal
numbers. A BigDecimal consists of an
arbitrary precision integer unscaled
value and a 32-bit integer scale. If
zero or positive, the scale is the
number of digits to the right of the
decimal point. If negative, the
unscaled value of the number is
multiplied by ten to the power of the
negation of the scale. The value of
the number represented by the
BigDecimal is therefore (unscaledValue
× 10^-scale).
There has been many questions on Stack Overflow relating to the matter of floating point numbers and its precision. Here is a list of related questions that may be of interest:
Why do I see a double variable initialized to some value like 21.4 as 21.399999618530273?
How to print really big numbers in C++
How is floating point stored? When does it matter?
Use Float or Decimal for Accounting Application Dollar Amount?
If you really want to get down to the nitty gritty details of floating point numbers, take a look at What Every Computer Scientist Should Know About Floating-Point Arithmetic.
When you input a double number, for example, 33.33333333333333, the value you get is actually the closest representable double-precision value, which is exactly:
33.3333333333333285963817615993320941925048828125
Dividing that by 100 gives:
0.333333333333333285963817615993320941925048828125
which also isn't representable as a double-precision number, so again it is rounded to the nearest representable value, which is exactly:
0.3333333333333332593184650249895639717578887939453125
When you print this value out, it gets rounded yet again to 17 decimal digits, giving:
0.33333333333333326
If you just want to process values as fractions, you can create a Fraction class which holds a numerator and denominator field.
Write methods for add, subtract, multiply and divide as well as a toDouble method. This way you can avoid floats during calculations.
EDIT: Quick implementation,
public class Fraction {
private int numerator;
private int denominator;
public Fraction(int n, int d){
numerator = n;
denominator = d;
}
public double toDouble(){
return ((double)numerator)/((double)denominator);
}
public static Fraction add(Fraction a, Fraction b){
if(a.denominator != b.denominator){
double aTop = b.denominator * a.numerator;
double bTop = a.denominator * b.numerator;
return new Fraction(aTop + bTop, a.denominator * b.denominator);
}
else{
return new Fraction(a.numerator + b.numerator, a.denominator);
}
}
public static Fraction divide(Fraction a, Fraction b){
return new Fraction(a.numerator * b.denominator, a.denominator * b.numerator);
}
public static Fraction multiply(Fraction a, Fraction b){
return new Fraction(a.numerator * b.numerator, a.denominator * b.denominator);
}
public static Fraction subtract(Fraction a, Fraction b){
if(a.denominator != b.denominator){
double aTop = b.denominator * a.numerator;
double bTop = a.denominator * b.numerator;
return new Fraction(aTop-bTop, a.denominator*b.denominator);
}
else{
return new Fraction(a.numerator - b.numerator, a.denominator);
}
}
}
Observe that you'd have the same problem if you used limited-precision decimal arithmetic, and wanted to deal with 1/3: 0.333333333 * 3 is 0.999999999, not 1.00000000.
Unfortunately, 5.6, 5.8 and 11.4 just aren't round numbers in binary, because they involve fifths. So the float representation of them isn't exact, just as 0.3333 isn't exactly 1/3.
If all the numbers you use are non-recurring decimals, and you want exact results, use BigDecimal. Or as others have said, if your values are like money in the sense that they're all a multiple of 0.01, or 0.001, or something, then multiply everything by a fixed power of 10 and use int or long (addition and subtraction are trivial: watch out for multiplication).
However, if you are happy with binary for the calculation, but you just want to print things out in a slightly friendlier format, try java.util.Formatter or String.format. In the format string specify a precision less than the full precision of a double. To 10 significant figures, say, 11.399999999999 is 11.4, so the result will be almost as accurate and more human-readable in cases where the binary result is very close to a value requiring only a few decimal places.
The precision to specify depends a bit on how much maths you've done with your numbers - in general the more you do, the more error will accumulate, but some algorithms accumulate it much faster than others (they're called "unstable" as opposed to "stable" with respect to rounding errors). If all you're doing is adding a few values, then I'd guess that dropping just one decimal place of precision will sort things out. Experiment.
You may want to look into using java's java.math.BigDecimal class if you really need precision math. Here is a good article from Oracle/Sun on the case for BigDecimal. While you can never represent 1/3 as someone mentioned, you can have the power to decide exactly how precise you want the result to be. setScale() is your friend.. :)
Ok, because I have way too much time on my hands at the moment here is a code example that relates to your question:
import java.math.BigDecimal;
/**
* Created by a wonderful programmer known as:
* Vincent Stoessel
* xaymaca#gmail.com
* on Mar 17, 2010 at 11:05:16 PM
*/
public class BigUp {
public static void main(String[] args) {
BigDecimal first, second, result ;
first = new BigDecimal("33.33333333333333") ;
second = new BigDecimal("100") ;
result = first.divide(second);
System.out.println("result is " + result);
//will print : result is 0.3333333333333333
}
}
and to plug my new favorite language, Groovy, here is a neater example of the same thing:
import java.math.BigDecimal
def first = new BigDecimal("33.33333333333333")
def second = new BigDecimal("100")
println "result is " + first/second // will print: result is 0.33333333333333
Pretty sure you could've made that into a three line example. :)
If you want exact precision, use BigDecimal. Otherwise, you can use ints multiplied by 10 ^ whatever precision you want.
As others have noted, not all decimal values can be represented as binary since decimal is based on powers of 10 and binary is based on powers of two.
If precision matters, use BigDecimal, but if you just want friendly output:
System.out.printf("%.2f\n", total);
Will give you:
11.40
You're running up against the precision limitation of type double.
Java.Math has some arbitrary-precision arithmetic facilities.
You can't, because 7.3 doesn't have a finite representation in binary. The closest you can get is 2054767329987789/2**48 = 7.3+1/1407374883553280.
Take a look at http://docs.python.org/tutorial/floatingpoint.html for a further explanation. (It's on the Python website, but Java and C++ have the same "problem".)
The solution depends on what exactly your problem is:
If it's that you just don't like seeing all those noise digits, then fix your string formatting. Don't display more than 15 significant digits (or 7 for float).
If it's that the inexactness of your numbers is breaking things like "if" statements, then you should write if (abs(x - 7.3) < TOLERANCE) instead of if (x == 7.3).
If you're working with money, then what you probably really want is decimal fixed point. Store an integer number of cents or whatever the smallest unit of your currency is.
(VERY UNLIKELY) If you need more than 53 significant bits (15-16 significant digits) of precision, then use a high-precision floating-point type, like BigDecimal.
private void getRound() {
// this is very simple and interesting
double a = 5, b = 3, c;
c = a / b;
System.out.println(" round val is " + c);
// round val is : 1.6666666666666667
// if you want to only two precision point with double we
// can use formate option in String
// which takes 2 parameters one is formte specifier which
// shows dicimal places another double value
String s = String.format("%.2f", c);
double val = Double.parseDouble(s);
System.out.println(" val is :" + val);
// now out put will be : val is :1.67
}
Use java.math.BigDecimal
Doubles are binary fractions internally, so they sometimes cannot represent decimal fractions to the exact decimal.
/*
0.8 1.2
0.7 1.3
0.7000000000000002 2.3
0.7999999999999998 4.2
*/
double adjust = fToInt + 1.0 - orgV;
// The following two lines works for me.
String s = String.format("%.2f", adjust);
double val = Double.parseDouble(s);
System.out.println(val); // output: 0.8, 0.7, 0.7, 0.8
Doubles are approximations of the decimal numbers in your Java source. You're seeing the consequence of the mismatch between the double (which is a binary-coded value) and your source (which is decimal-coded).
Java's producing the closest binary approximation. You can use the java.text.DecimalFormat to display a better-looking decimal value.
Short answer: Always use BigDecimal and make sure you are using the constructor with String argument, not the double one.
Back to your example, the following code will print 11.4, as you wish.
public class doublePrecision {
public static void main(String[] args) {
BigDecimal total = new BigDecimal("0");
total = total.add(new BigDecimal("5.6"));
total = total.add(new BigDecimal("5.8"));
System.out.println(total);
}
}
Multiply everything by 100 and store it in a long as cents.
Computers store numbers in binary and can't actually represent numbers such as 33.333333333 or 100.0 exactly. This is one of the tricky things about using doubles. You will have to just round the answer before showing it to a user. Luckily in most applications, you don't need that many decimal places anyhow.
Floating point numbers differ from real numbers in that for any given floating point number there is a next higher floating point number. Same as integers. There's no integer between 1 and 2.
There's no way to represent 1/3 as a float. There's a float below it and there's a float above it, and there's a certain distance between them. And 1/3 is in that space.
Apfloat for Java claims to work with arbitrary precision floating point numbers, but I've never used it. Probably worth a look.
http://www.apfloat.org/apfloat_java/
A similar question was asked here before
Java floating point high precision library
Use a BigDecimal. It even lets you specify rounding rules (like ROUND_HALF_EVEN, which will minimize statistical error by rounding to the even neighbor if both are the same distance; i.e. both 1.5 and 2.5 round to 2).
Why not use the round() method from Math class?
// The number of 0s determines how many digits you want after the floating point
// (here one digit)
total = (double)Math.round(total * 10) / 10;
System.out.println(total); // prints 11.4
Check out BigDecimal, it handles problems dealing with floating point arithmetic like that.
The new call would look like this:
term[number].coefficient.add(co);
Use setScale() to set the number of decimal place precision to be used.
If you have no choice other than using double values, can use the below code.
public static double sumDouble(double value1, double value2) {
double sum = 0.0;
String value1Str = Double.toString(value1);
int decimalIndex = value1Str.indexOf(".");
int value1Precision = 0;
if (decimalIndex != -1) {
value1Precision = (value1Str.length() - 1) - decimalIndex;
}
String value2Str = Double.toString(value2);
decimalIndex = value2Str.indexOf(".");
int value2Precision = 0;
if (decimalIndex != -1) {
value2Precision = (value2Str.length() - 1) - decimalIndex;
}
int maxPrecision = value1Precision > value2Precision ? value1Precision : value2Precision;
sum = value1 + value2;
String s = String.format("%." + maxPrecision + "f", sum);
sum = Double.parseDouble(s);
return sum;
}
You can Do the Following!
System.out.println(String.format("%.12f", total));
if you change the decimal value here %.12f
So far I understand it as main goal to get correct double from wrong double.
Look for my solution how to get correct value from "approximate" wrong value - if it is real floating point it rounds last digit - counted from all digits - counting before dot and try to keep max possible digits after dot - hope that it is enough precision for most cases:
public static double roundError(double value) {
BigDecimal valueBigDecimal = new BigDecimal(Double.toString(value));
String valueString = valueBigDecimal.toPlainString();
if (!valueString.contains(".")) return value;
String[] valueArray = valueString.split("[.]");
int places = 16;
places -= valueArray[0].length();
if ("56789".contains("" + valueArray[0].charAt(valueArray[0].length() - 1))) places--;
//System.out.println("Rounding " + value + "(" + valueString + ") to " + places + " places");
return valueBigDecimal.setScale(places, RoundingMode.HALF_UP).doubleValue();
}
I know it is long code, sure not best, maybe someone can fix it to be more elegant. Anyway it is working, see examples:
roundError(5.6+5.8) = 11.399999999999999 = 11.4
roundError(0.4-0.3) = 0.10000000000000003 = 0.1
roundError(37235.137567000005) = 37235.137567
roundError(1/3) 0.3333333333333333 = 0.333333333333333
roundError(3723513756.7000005) = 3.7235137567E9 (3723513756.7)
roundError(3723513756123.7000005) = 3.7235137561237E12 (3723513756123.7)
roundError(372351375612.7000005) = 3.723513756127E11 (372351375612.7)
roundError(1.7976931348623157) = 1.797693134862316
Do not waste your efford using BigDecimal. In 99.99999% cases you don't need it. java double type is of cource approximate but in almost all cases, it is sufficiently precise. Mind that your have an error at 14th significant digit. This is really negligible!
To get nice output use:
System.out.printf("%.2f\n", total);

Categories

Resources