I was trying out subtracting numbers in java, and this gives me unexpected result
public class FloatWeird
{
public static void main(String[] args)
{
double n = 1;
for(int i = 0;i<10;i++)
{
System.out.println(n);
n = n - 0.10;
}
}
}
Result
1.0
0.9
0.8
0.7000000000000001
0.6000000000000001
0.5000000000000001
0.40000000000000013
0.30000000000000016
0.20000000000000015
0.10000000000000014
I have gone through a few forums and understand that using the BigDecimal class is one solution. However, is there a way to correct it in a simpler way using double as above?
I suggest you use appropriate rounding.
System.out.printf("%.1f%n", n);
When ever you print a double you have to consider what the appropriate round is.
You can also round the result as you calculate which is what BigDecimal does.
n = n - 0.10;
n = Math.round(n * 10) / 10.0;
This will reduce cumulative error.
Another approach is to work in a different unit, e.g. instead of dollar you use cents. This means you can use an int or long and only convert
to dollars for presentation.
long n = 100;
for(int i = 0; i < 10; i++) {
System.out.println(n / 100.0);
n = n - 10;
}
this prints http://ideone.com/Uf70jC
1.0
0.9
0.8
0.7
0.6
0.5
0.4
0.3
0.2
0.1
Note: even if you use BigDecimal, this still have to do this except the API can help you determine at what point you should do this.
Related
I have a custom made BigRational class in java.
It is implemented as two BigInteger, representing numerator and denominator.
I have a "from string" method that take input in the form "-1234/43"
but I would like to implement a from double/from float;
I'm not scare of generating a very large number, but I would like to keep all the precision present in the floating point representation; thus if I converted them in some decimal representation I would lose precision thanks to rounding.
-How do I generate a pair of BigIntegers that interpreted as numerator/denominator represents the same exact number as a given float/double?
(Hopefully by being in Java I do not need to worry about bigendian/littleendian, but I would like a confermation too)
So, thanks to a good friend I have found a good solution, so I will post it here for anyone in need.
It is not using any string representation so it should also be quite on the fast side.
I have tested it "reasonably" and It seams to work and to keep the exact representation.
Of course, we should still add some 'if' to handle NANs.
final static int mantissaBits=53;
public static BigRational from(double num){
int exponent=Math.getExponent(num);
long man=Math.round(Math.scalb(num, mantissaBits-exponent));
long den=Math.round(Math.scalb(1.0, mantissaBits-exponent));
return new BigRational(BigInteger.valueOf(man),BigInteger.valueOf(den));
}
Caveat: Not all numbers are rational, e.g. PI is not a rational number. However, given that double (and float) have limited precision, there are a limited number of digits in a floating-point value, so you can always find a rational number for that. E.g. Math.PI is a double with the value 3.141592653589793. That number is the rational number 3_141_592_653_589_793 / 1_000_000_000_000_000.
Understanding the caveat that floating-point values aren't accurate, you can find the rational number with the help of BigDecimal, then normalize the rational number using BigInteger.gcd().
Like this:
static void printAsRational(double value) {
printAsRational(BigDecimal.valueOf(value));
}
static void printAsRational(float value) {
printAsRational(new BigDecimal(Float.toString(value)));
}
static void printAsRational(BigDecimal value) {
BigInteger numerator, denominator;
if (value.signum() == 0) {
// Zero is 0 / 1
numerator = BigInteger.ZERO;
denominator = BigInteger.ONE;
} else {
BigDecimal bd = value.stripTrailingZeros(); // E.g. 1.20 -> 1.2
if (bd.scale() < 0)
bd = bd.setScale(0); // E.g. 1.7e3 -> 1700
numerator = bd.unscaledValue(); // E.g. 1.25 -> 125
denominator = BigDecimal.valueOf(1, -bd.scale()).toBigInteger(); // E.g. 1.25 -> 100
// Normalize, e.g. 12/8 -> 3/2
BigInteger gcd = numerator.gcd(denominator);
if (! gcd.equals(BigInteger.ONE)) {
numerator = numerator.divide(gcd);
denominator = denominator.divide(gcd);
}
}
System.out.println(value + " = " + numerator + " / " + denominator);
}
Tests
printAsRational(Math.PI);
printAsRational(Math.E);
printAsRational(1.25);
printAsRational(1);
printAsRational(0);
printAsRational(-1.25);
printAsRational(1.25e9);
printAsRational(1.25e-9);
Output
3.141592653589793 = 3141592653589793 / 1000000000000000
2.718281828459045 = 543656365691809 / 200000000000000
1.25 = 5 / 4
1.0 = 1 / 1
0.0 = 0 / 1
-1.25 = -5 / 4
1.25E+9 = 1250000000 / 1
1.25E-9 = 1 / 800000000
I'm implementing Babylonian method to approximate the square root of number n using following formula :
nextGuess = (lastGuess + n / lastGuess) / 2;
So when nextGuess and lasGuess are almost identical, nextGuess is the approximated square root.
What am doing is checking if nextGuess and lastGuess is less than very small number such as 0.0001 then i can claim that nextGuess is the approximated square root of n. if not nextGuess become lastGuess.
So how i can implement that in the right way?
My current code:
public static void getApproximatedSquare(long n){
DecimalFormat decimalFormat = new DecimalFormat("#.####");
decimalFormat.setRoundingMode(RoundingMode.CEILING);
double lastGuess = 1, nextGuess;
nextGuess = (lastGuess + n / lastGuess) / 2;
Double init = 0.0001;
System.out.println(decimalFormat.format(init));
if (Double.valueOf(decimalFormat.format(nextGuess)) <= init)
//todo
}
Current draft of implementation has a few flaws:
I doubt you really need Double.valueOf(decimalFormat.format(...)), it just removes some precision from the result
Your convergence condition is not nextGuess < init but difference_between_nextGuess_and_lastGuess < init
You have to repeat the approximation until convergence, so you can't use just a if. You need a for or while or (as in my solution) do... while
This should work (at each step, it prints last and next guesses)
public static double getApproximatedSquare(long n) {
DecimalFormat decimalFormat = new DecimalFormat("#.####");
decimalFormat.setRoundingMode(RoundingMode.CEILING);
double lastGuess, nextGuess = 1;
double init = 0.0001;
do {
lastGuess = nextGuess;
nextGuess = (lastGuess + (double) n / lastGuess) / 2;
System.out.println(decimalFormat.format(lastGuess)+" ---> "+decimalFormat.format(nextGuess));
} while (Math.abs(lastGuess - nextGuess) >= init);
return nextGuess;
}
Using an absolute tolerance is always a bad idea because it doesn't take into account the order of magnitude of the argument. A relative error is better.
But in the case of the square root, I recommend a much better approach: make sure that your initial approximation is within a factor √2 of the exact root. This is obtained by halving the exponent of 2 in the floating-point representation of the argument. (If you can't access this exponent, you can obtain it by successive divisions or multiplications until to reach the interval [1, 2).)
Example: for 27, you have 16 ≤ 27 < 32. Then 1 ≤ √27 / 4 < √2, and you can start the iterations from 4.
Then perform four iterations of the Babylonian formula. No less, no more.
In the example, after four iterations, you obtain 5.19615242271, which is exact.
If you have the feeling that the successive halving or doubling process is slow and believe that Newton is faster, consider that (x + n / x) / 2 > x / 2, so that Newton actually converges slower than halvings and involves more arithmetic
!
If nextGuess's value is 100% sure to go down and reach a good enough value, can't you just do this?
public static void getApproximatedSquare(long n){
DecimalFormat decimalFormat = new DecimalFormat("#.####");
decimalFormat.setRoundingMode(RoundingMode.CEILING);
double lastGuess = n + 1;
double nextGuess = n;
double init = 0.0001;
while (lastGuess - nextGuess > init)
{
lastGuess = nextGuess;
nextGuess = (lastGuess + n / lastGuess) / 2;
}
System.out.println(decimalFormat.format(init));
}
As nextGuess approaches sqrt(n) from above, you can use this:
double lastGuess = n;
double nextGuess = n;
double epsilon = 1e-4;
while (lastGuess - nextGuess > epsilon) {
lastGuess = nextGuess;
nextGuess = (lastGuess + n / lastGuess) / 2;
}
I have a method that returns the factorial of the input. It works perfectly for integers, but I cant figure out how to make it work with decimal numbers.
Here is my method currently:
public static double factorial(double d)
{
if (d == 0.0)
{
return 1.0;
}
double abs = Math.abs(d);
double decimal = abs - Math.floor(abs);
double result = 1.0;
for (double i = Math.floor(abs); i > decimal; --i)
{
result *= (i + decimal);
}
if (d < 0.0)
{
result = -result;
}
return result;
}
I found an implementation but the code wasn't shown (I lost the link) and the example given was 5.5! = 5.5 * 4.5 * 3.5 * 2.5 * 1.5*0.5! = 287.885278
So from this pattern, I just added the decimal value to i in the for-loop result *= (i + decimal)
But clearly my logic is flawed
Edit: Just realsed that the last value is 0.5!, not 0.5. This makes all the difference. So 0.5! = 0.88622 and 5.5! = 5.5 * 4.5 * 3.5 * 2.5 * 1.5 * 0.88622 which equals 287.883028125
The gamma function (which generalizes factorials to real numbers) is rather tricky to implement directly. Use a library such as apache-commons-math to calculate it for you, or look at their source to get a feel of what is involved. Once available, use as follows:
public static double generalizedFactorial(double d) {
// Gamma(n) = (n-1)! for integer n
return Gamma.gamma(d+1);
}
Outputs:
4.0! = 24.0
5.5! = 287.88527781504433
6.0! = 720.0
Previous answer (provides a factorial-like interpretation for real numbers > 1; but since there is already an aggreed-upon extension of factorial to real numbers, please disregard this for anything practical):
public static double f(double d) {
double r = d - Math.floor(d) + 1;
for (;d>1; d-=1) {
r *= d;
}
return r;
}
Outputs:
4.0! = 24.0
5.5! = 487.265625
6.0! = 720.0
Factorial isn't actually defined for x€R, only for x€N. In words: you can't calculate the factorial of a decimal. (You can for 5.0, but not 5.1)
Edit: Thats the view for "traditional" factorial, for (really rarely needed) decimal factorial, see "gamma function".
I am doing a school assignment in which I solve an equation involving coordinates in a circle (r^2 = x^2 + y^2) were r = 1, and you increment through x values solving for y. I am getting a repeating decimals even though I only incrementing in tenths. I have no idea why and have tried it in a few different ways. Here is the code.
double r = 1;
double rSqr;
double x = 1;
double xSqr;
double y;
double ySqr;
double inc = 0.1;
int count = 0;
while(x > -r)
{
x = r - (count * inc);
rSqr = Math.pow(r, 2);
xSqr = Math.pow(x, 2);
ySqr = rSqr - xSqr;
y = Math.sqrt(ySqr);
count++;
System.out.println(x + " " + y);
}
and the output is this
1.0 0.0
0.9 0.4358898943540673
0.8 0.5999999999999999
0.7 0.714142842854285
0.6 0.8
0.5 0.8660254037844386
0.3999999999999999 0.9165151389911681
0.29999999999999993 0.9539392014169457
0.19999999999999996 0.9797958971132712
0.09999999999999998 0.99498743710662
0.0 1.0
-0.10000000000000009 0.99498743710662
-0.20000000000000018 0.9797958971132712
-0.30000000000000004 0.9539392014169457
-0.40000000000000013 0.9165151389911679
-0.5 0.8660254037844386
-0.6000000000000001 0.7999999999999999
-0.7000000000000002 0.7141428428542849
-0.8 0.5999999999999999
-0.9000000000000001 0.43588989435406705
-1.0 0.0
The problem is that double is imprecise. It uses 64 bits to represent decimal numbers; some bits are used for the numeric part, and some for the exponent, but many seemingly simple decimal numbers can not be accurately represented in this way, for example 0.1. See this wiki article for more.
One way around the problem is to display the number using DecimalFormat, which can round the number for presentation purposes. Here's some example code:
public static void main(String[] args) {
DecimalFormat decimalFormat = new DecimalFormat("#0.000");
double d = 1 - .9; // one way to get a repeating decimal floating point number
System.out.println(d);
System.out.println(decimalFormat.format(d));
}
Output:
0.09999999999999998
0.100
It is the IEEE 754 floating point representation.
Use BigDecimal as datatype instead of double to solve your problem.
But take care as BigDecimal is immutable.
BigDecimal r = BigDecimal.ONE;
BigDecimal rSqr;
BigDecimal x = BigDecimal.ONE;
BigDecimal xSqr;
BigDecimal y;
BigDecimal ySqr;
BigDecimal inc = new BigDecimal("0.1");
int count = 0;
while(x.compareTo(r.negate())>0)
{
// i'll let you fill in this part
}
I found that a rounding error with my Java application. The method used to round was:
public static double round(double value,double precision)
{
return Math.round(value * precision) / precision;
}
This could have an error (i.e. round(138.515,100) should return 138.52, and returns 138.51)
So I've created the following rounder:
// Mikeldi's rounder
public static double round2DecimalPlaces(double value,int decimalPlaces)
{
int s = value<0?-1:1;
double p = 1;
for (int i = 0; i < decimalPlaces; i++) {
p*=10;
}
double n = (long) value;
double d = ((value*10*p)-(n*10*p));
d +=s*5;
d /= 10;
d = (long)d;
d /= p;
return d+n;
}
I created this method since other rounding methods added too much latency to the system (low latency system). This one is around 10 times faster than the previous.
Note: This rounder will only use to round to possitive decimalPlaces (or 0).
Is there any problems I haven't see with this new rounder?
Thanks,
The Math#round method is not broken. 138.515 can't be exactly represented as a double. To see the exact value, you can use:
System.out.println(new BigDecimal(138.515d));
which prints:
138.5149999999999863575794734060764312744140625
It is therefore accurate for round to return 138.51. If you need more precision than double can give, you can use BigDecimal.
EDIT
If BigDecimal is not an option, and if the number of decimals is smallish (say 3 or 4 because these are prices for example), you can use longs instead with the last 4 digits being the decimals. So 138.51d would be 1385100L instead.