Is there a precision difference between the following (assuming the value of a and b can be represented without loss of precision in a float).
With floats:
float a;
float b;
double result = 1 + a*b;
With doubles:
double a;
double b;
double result = 1 + a*b;
Simple example:
float a = 16777217; // Largest int exactly representable in a float.
float b = 16777217;
System.out.println((double)(1 + a*b));
double c = 16777217;
double d = 16777217;
System.out.println(1 + c*d);
Output (Ideone):
2.81474976710656E14
2.8147501026509E14
So yes, there is a loss of precision using float.
There is a loss of precision in
float a;
float b;
double result = 1 + a*b;
the float representation.
the product of a and b which will also be a float. Note: a * b is a float
the addition on 1 could result in a loss of precision.
To should that a * b can lose more precision
for (int i = 1; i < 100; i += 2) {
float a = i;
float b = 1.0f / i;
if ((double) a * b != a * b && a * b != 1)
System.out.println(i + " " + (double) a * b + " " + a * b);
}
prints
41 0.999999962747097 0.99999994
47 0.9999999683350325 0.99999994
55 0.9999999683350325 0.99999994
61 0.9999999441206455 0.99999994
83 0.999999962747097 0.99999994
97 0.999999969266355 0.99999994
note: it could also happen recover precision lost and get the right answer after b has lost precision
for (int i = 1; i < 20; i += 2) {
float a = i;
float b = 1.0f / i;
if (b != 1.0 / i && a * b == 1)
System.out.println(i + " " + (double) a * b + " " + a * b);
}
prints
3 1.0000000298023224 1.0
5 1.0000000149011612 1.0
7 1.0000000447034836 1.0
9 1.0000000074505806 1.0
11 1.0000000298023224 1.0
13 1.000000037252903 1.0
15 1.0000000521540642 1.0
17 1.0000000037252903 1.0
19 1.0000000074505806 1.0
There can be a loss of precision difference in a*b part when you evaluating it as floats and doubles. So yes, with some values, 2nd one will be more accurate.
Related
I have a problem which requires me to calculate the amount of times a number must be added to a coordinate x for the floored value of x to change. It must support both positive and negative numbers being added.
The example code below highlights the problem.
public static void main(String[] args) {
double x = 10;
double amountToAdd = -0.25; // must support positive + negative values
int lastFlooredX = Integer.MIN_VALUE;
int i = 0;
while (i++ < 100) {
int flooredX = (int) Math.floor(x);
if (lastFlooredX == flooredX) {
throw new UnsupportedOperationException("Iterated over the same number twice!");
}
lastFlooredX = flooredX;
int additionsBeforeFloorChanges = // I need help!;
x += (amountToAdd * additionsBeforeFloorChanges);
}
}
The problem with the additive approach is that it's very susceptible to floating point rounding error.
Consider this simple approach for +ve x and d:
double xfloor = Math.floor(x);
int count = 0;
do
{
x += d;
count += 1;
System.out.println(count + " " + x);
}
while(Math.floor(x) == xfloor);
System.out.format("Result: %d%n", count);
With x = 0 and d = 0.1 this produces:
1 0.1
2 0.2
3 0.30000000000000004
4 0.4
5 0.5
6 0.6
7 0.7
8 0.7999999999999999
9 0.8999999999999999
10 0.9999999999999999
11 1.0999999999999999
Result: 11
Which obviously is not correct.
As an alternative, we can use division to determine the number of steps required. We have to be careful to distinguish between cases where x and d have the same or opposite signs.
static int minAddFloorChange(double x, double d)
{
if(d == 0) throw new IllegalArgumentException();
double xa = Math.abs(x);
double da = Math.abs(d);
if(x == 0 || (Math.signum(x) == Math.signum(d)))
{
double xfrac = (xa == Math.rint(xa)) ? 1 : Math.ceil(xa) - xa;
double count = xfrac / da;
return (int)Math.ceil(count);
}
else
{
double xfrac = (x == Math.rint(xa)) ? 0 : xa - Math.floor(xa);
double count = xfrac / da;
return Math.max(1, (int)Math.ceil(count));
}
}
Note, I'm not claiming that this approach is completely immune from rounding errors - you'd need someone with more knowledge of the IEEE 754 floating point format than I have to address that.
Test:
static void test(double x, double d)
{
System.out.println(x + " : " + d + " = " + minAddFloorChange(x, d));
}
public static void main(String[] args)
{
test(0, 0.1);
test(0, -0.1);
test(1, 0.1);
test(-1, -0.1);
test(1, -0.1);
test(-1, 0.1);
test(0, +Double.MIN_VALUE);
test(0, -Double.MIN_VALUE);
test(1, Double.MIN_VALUE);
test(-1, -Double.MIN_VALUE);
test(1, -Double.MIN_VALUE);
test(-1, Double.MIN_VALUE);
}
Output:
0.0 : 0.1 = 10
0.0 : -0.1 = 10
1.0 : 0.1 = 10
-1.0 : -0.1 = 10
1.0 : -0.1 = 1
-1.0 : 0.1 = 1
0.0 : 4.9E-324 = 2147483647
0.0 : -4.9E-324 = 2147483647
1.0 : 4.9E-324 = 2147483647
-1.0 : -4.9E-324 = 2147483647
1.0 : -4.9E-324 = 1
-1.0 : 4.9E-324 = 1
public static void main(String[] args) {
double x = 10.1;
double amountToAdd = -0.25; // must support positive+negative values
double y = x;
int i = 0;
if(x-Math.floor(x)!=0) {
while(Math.abs(Math.floor(y)-Math.floor(x))==0) {
i++;
y = y +amountToAdd;
}
}
System.out.println(i);
}
for amountToAddas -0.25 output and x as 10.1
1
for amountToAddas 0.25 output and x as 10.1
4
this is the question.
Write a Java program to declare two integers and demonstrate the use of
addition, subtraction, multiplication, division and modulo operators.
Repeat the calculations from task 1 using decimal numbers.
So I've done part one:
public static void main(String[] args)
{
float a = 4 + 1;
float b = 484 - 48;
float c = 484 * 49;
float d = 32 / 93;
float e = 55 % 787;
System.out.print(a + "\n" + b + "\n" + c + "\n" + d + "\n" + e);
}
but I'm stuck in part two.
Integers are of type int. You are using float and declaring 5 variables. Decimal numbers are type double
public static void main(String[] args)
{
int a = 1;
int b = 2;
int out = a*b;
System.out.print(out);
}
Repeat using doubles
double a = 1.5;
double b = 2.5;
I have made a function that converts a double to a simplified fraction in Java:
public static int gcm(int a, int b) {
return b == 0 ? a : gcm(b, a % b);
}
public static String toFraction(double d) {
int decimals = String.valueOf(d).split("\\.")[1].length();
int mult = (int) Math.pow(10, decimals);
int numerator = (int) (d * mult);
int denominator = mult;
// now simplify
int gcm = gcm(numerator, denominator);
numerator /= gcm;
denominator /= gcm;
return numerator + "/" + denominator;
}
It works, except for the fact that if I use toFraction(1.0/3), this will, understandably, return "715827882/2147483647". How may I fix this to return "1/3"?
You have to allow for a certain error and not all fractions can be exactly represented as scalar values.
public static String toFraction(double d, double err) {
String s = Long.toString((long) d);
d -= (long) d;
if (d > err) {
for (int den = 2, max = (int) (1 / err); den < max; den++) {
long num = Math.round(d * den);
double d2 = (double) num / den;
if (Math.abs(d - d2) <= err)
return (s.equals("0") ? "" : s + " ") + num +"/"+den;
}
}
return s;
}
public static void main(String... args) {
System.out.println(toFraction(1.0/3, 1e-6));
System.out.println(toFraction(1.23456789, 1e-6));
System.out.println(toFraction(Math.E, 1e-6));
System.out.println(toFraction(Math.PI, 1e-6));
for (double d = 10; d < 1e15; d *= 10)
System.out.println(toFraction(Math.PI, 1.0 / d));
}
prints
1/3
1 19/81
2 719/1001
3 16/113
3 1/5
3 1/7
3 9/64
3 15/106
3 16/113
3 16/113
3 3423/24175
3 4543/32085
3 4687/33102
3 14093/99532
3 37576/265381
3 192583/1360120
3 244252/1725033
3 2635103/18610450
Note: this finds the 21/7, 333/106 and 355/113 approximations for PI.
No double value is equal to one third, so the only way your program can be made to print 1/3 is if you change the specification of the method to favour "nice" answers rather than the answer that is technically correct.
One thing you could do is choose a maximum denominator for the answers, say 100, and return the closest fraction with denominator 100 or less.
Here is how you could implement this using Java 8 streams:
public static String toFraction(double val) {
int b = IntStream.rangeClosed(1, 100)
.boxed()
.min(Comparator.comparingDouble(n -> Math.abs(val * n - Math.round(val * n))))
.get();
int a = (int) Math.round(val * b);
int h = gcm(a, b);
return a/h + "/" + b/h;
}
There is no nice approach to this. double is not very good for this sort of thing. Note that BigDecimal can't represent 1/3 either, so you'll have the same problem with that class.
There are nice ways to handle this but you will need to look at special cases. For example, if the numerator is 1 then the fraction is already reduced and you simply strip out the decimal places and return what you were given.
I saw this question of mapping numbers algorithm.
I am trying to implement the #PeterAllenWebb solution in java like this:
long A = 1l;
long B = 999999999l;
long C = 1000000000l;
long D = 9999999999l;
long X = 999999998l;
long Y = (D-C)*(X-A)/(B-A) + C;
System.out.println("original is " + X);
long reverseX = (B-A)*(Y-C)/(D-C) + A;
System.out.println("reverse is " + reverseX);
However, this doesn't always work.
See below:
X reverseX
999999998 999999997
1 1
999999999 999999999
12 11
As you can see, only minimum (A) and maximum (B) are returning fine.
For the rest, I need to add 1. This seems to me as floor/round/Math issue and I don't want to rely on the JVM that calculates it. I would like it to always work.
How can I make the above work for reverseX?
You are facing an age old issue. By default division is double in Java. So if your division result is 1.0 or 1.3 or 1.9 it will be truncated to 1. In your case the same is happening. Try changing it to double from long as below
double A = 1L;
double B = 999999999l;
double C = 1000000000l;
double D = 9999999999l;
double X = 999999998l;
double Y = (D - C) * (X - A) / (B - A) + C;
System.out.println("original is " + new DecimalFormat("#").format(X));
double reverseX = (B - A) * (Y - C) / (D - C) + A;
System.out.println("reverse is "
+ new DecimalFormat("#").format(reverseX));
I have this piece of code and according to this page here
The below output should by right give me, 98.24 but this is giving me 68.8, what is that I am missing here?
public class Qn1
{
public static void main(String[] args)
{
double cel = 36.8;
double fah = ((9 / 5 )* cel) + 32;
System.out.println(cel + "deg C =" + fah +" deg F");
}
}
Use 9.0 / 5 instead of 9 / 5 in bracket.
9 / 5 is integer division and its value is 1. And hence the result. You just need to make one of the numerator or denominator a double / float value to enforce floating-point division.
((9 / 5 ) * cel) + 32 = (1 * 36.8) + 32 = 68.8
And what you need is: -
((9.0 / 5 ) * cel) + 32 = (1.8 * 36.8) + 32 = 66.24 + 32 = 98.24
double fah = ((9.0 / 5 )* cel) + 32;
The problem is you are not using double but int. Use
double fah = ((9d / 5d) * cel) + 32d;
Use at least one double operand:
double fah = 9.0 / 5 * cel + 32;
double fah = 9 / 5.0 * cel + 32;
double fah = 9.0 / 5.0 * cel + 32;
These three ways are valid and note that parenthesis are unnecessary.
9 is integer, so is 5 : so 9/5 is using integer division, meaning it results into 1 (integer) and not 1.8 (float)
1*36.8 +32 = 68.8
9 / 5 in integer arithmetic is 1