Win a copy of Testing JavaScript Applications this week in the HTML Pages with CSS and JavaScript forum!
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Bear Bibeault
  • Ron McLeod
  • Jeanne Boyarsky
  • Paul Clapham
Sheriffs:
  • Tim Cooke
  • Liutauras Vilda
  • Junilu Lacar
Saloon Keepers:
  • Tim Moores
  • Stephan van Hulst
  • Tim Holloway
  • fred rosenberger
  • salvin francis
Bartenders:
  • Piet Souris
  • Frits Walraven
  • Carey Brown

Having trouble with floating point precision.

 
Greenhorn
Posts: 13
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hello, I am making a program that calculates the four points of a circle with a radius (double) given by the user, and an increment (double) of the x value given by the user. So if the radius is 1.0 and the increment is 0.1, it will find the points for all radii between 1.0 and -1.0 with increments of 0.1. Except that it doesn't work with different radius and increment values. This is happening with the x1 double as the increment is being subtracted from it in the for loop.

 
Saloon Keeper
Posts: 12161
258
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Welcome to CodeRanch!

ItDoesntWorkIsUseless. Please tell us what output you were expecting and what you got instead.
 
Perry Graham
Greenhorn
Posts: 13
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Specifically looking at the x1 values, using the 1.0 radius and 0.1 increment gets me the desired output which is.
   x1      y1      x1      y2
-------------------------------------
  1.00    0.00    1.00    0.00
  0.90    0.44    0.90   -0.44
  0.80    0.60    0.80   -0.60
  0.70    0.71    0.70   -0.71
  0.60    0.80    0.60   -0.80
  0.50    0.87    0.50   -0.87
  0.40    0.92    0.40   -0.92
  0.30    0.95    0.30   -0.95
  0.20    0.98    0.20   -0.98
  0.10    0.99    0.10   -0.99
  0.00    1.00    0.00   -1.00
 -0.10    0.99   -0.10   -0.99
 -0.20    0.98   -0.20   -0.98
 -0.30    0.95   -0.30   -0.95
 -0.40    0.92   -0.40   -0.92
 -0.50    0.87   -0.50   -0.87
 -0.60    0.80   -0.60   -0.80
 -0.70    0.71   -0.70   -0.71
 -0.80    0.60   -0.80   -0.60
 -0.90    0.44   -0.90   -0.44
 -1.00    0.00   -1.00   -0.00

As you can see the x1 values decrement exactly how it should be. However if I use a 1.5 radius and 0.1 increment it gives me:
   x1      y1      x1      y2
-------------------------------------
  1.50    0.00    1.50    0.00
  1.40    0.54    1.40   -0.54
  1.30    0.75    1.30   -0.75
  1.20    0.90    1.20   -0.90
  1.10    1.02    1.10   -1.02
  1.00    1.12    1.00   -1.12
  0.90    1.20    0.90   -1.20
  0.80    1.27    0.80   -1.27
  0.70    1.33    0.70   -1.33
  0.60    1.37    0.60   -1.37
  0.50    1.41    0.50   -1.41
  0.40    1.45    0.40   -1.45
  0.30    1.47    0.30   -1.47
  0.20    1.49    0.20   -1.49
  0.10    1.50    0.10   -1.50
 -0.00    1.50   -0.00   -1.50
 -0.10    1.50   -0.10   -1.50
 -0.20    1.49   -0.20   -1.49
 -0.30    1.47   -0.30   -1.47
 -0.40    1.45   -0.40   -1.45
 -0.50    1.41   -0.50   -1.41
 -0.60    1.37   -0.60   -1.37
 -0.70    1.33   -0.70   -1.33
 -0.80    1.27   -0.80   -1.27
 -0.90    1.20   -0.90   -1.20
 -1.00    1.12   -1.00   -1.12
 -1.10    1.02   -1.10   -1.02
 -1.20    0.90   -1.20   -0.90
 -1.30    0.75   -1.30   -0.75
 -1.40    0.54   -1.40   -0.54
Which this is what I don't want because it gives me a -0.00 and it is missing the final number at the end.

When looking at just the x1 values for 1.0 radius and 0.1 increment I get:
1.0
0.9
0.8
0.7000000000000001
0.6000000000000001
0.5000000000000001
0.40000000000000013
0.30000000000000016
0.20000000000000015
0.10000000000000014
1.3877787807814457E-16
-0.09999999999999987
-0.19999999999999987
-0.2999999999999999
-0.3999999999999999
-0.4999999999999999
-0.5999999999999999
-0.6999999999999998
-0.7999999999999998
-0.8999999999999998
-0.9999999999999998

Which due to floating point imprecision is not ideal but still gives me the desired output.
While looking at just the x1 values for 1.5 radius and 0.1 increment gets me:

1.5
1.4
1.2999999999999998
1.1999999999999997
1.0999999999999996
0.9999999999999997
0.8999999999999997
0.7999999999999997
0.6999999999999997
0.5999999999999998
0.4999999999999998
0.3999999999999998
0.2999999999999998
0.19999999999999982
0.09999999999999981
-1.942890293094024E-16
-0.1000000000000002
-0.2000000000000002
-0.3000000000000002
-0.40000000000000024
-0.5000000000000002
-0.6000000000000002
-0.7000000000000002
-0.8000000000000002
-0.9000000000000001
-1.0000000000000002
-1.1000000000000003
-1.2000000000000004
-1.3000000000000005
-1.4000000000000006

I guess my answer would have to be something that addresses the x1 -= increment part of the for-loop, but I'm not exactly sure how to tackle that.
 
Marshal
Posts: 25682
69
Eclipse IDE Firefox Browser MySQL Database
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Perry Graham wrote:I guess my answer would have to be something that addresses the x1 -= increment part of the for-loop, but I'm not exactly sure how to tackle that.



No, what you want to address is how you tell when you reach the end of the loop. Since adding 0.1 ten times doesn't give you 1.0, but only something very close to it, you need to realize that the loop ends when you have a value which is very close to the end value. (And, "very close" may be just below or just above the end value.)
 
Marshal
Posts: 69874
278
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Welcome to the Ranch

That is a notorious instance where floating‑point arithmetic goes wrong.Only use floating‑point arithmetic when you don't mind your results being slightly out. Most definitely don't use floating‑point numbers for money, nor for loop variants.
Avoid strange arithmetic and strange inequality operators inside a loop. The more you useor multiplication in the loop header, the more chances you have to get it wrong. If at all possible stick toUse integer arithmetic for the loop header. Work out your radius in the loop body:-Don't use Math#pow(x, 2) for squares when you will usually get faster performance with x * x. Don't work out your own Pythagoras arithmetic if you can use this instead. You shouldn't need Math#abs() because (I think) Math#sqrt() returns the positive square root. You can verify that here.
 
Saloon Keeper
Posts: 22284
151
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Campbell Ritchie wrote:Welcome to the Ranch
Don't use Math#pow(x, 2) for squares when you will usually get faster performance with x * x.



Actually, having disassembled old Fortran libraries, I can say that it's not uncommon for the power function to optimize itself by checking for common integral exponent amounts and doing the "x * x" itself. Benchmark it if you aren't sure - although unless the compiler is one that also optimizes the math library (and that, too has been known to happen), you'll still incur an overhead for the function call and the exponent tests. Failing that, pow() has to convert the base to a logarithm, multiply by the exponent, then get the anti-logarithm and that is a lot more work, even if it's all done in the FPU.

The reason why you should never use floating-point in bookkeeping functions is that the only decimal fraction of 10 that can be accurately stored to one decimal point is 0.5. The Babylonians knew what they were doing using 60 for a base.

Don't use floating-point numbers as database keys, either.
 
Bartender
Posts: 4006
156
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
If you want to have the line segments of the circle to have constant length, you can also use polar coordinates. For instance:

 
Campbell Ritchie
Marshal
Posts: 69874
278
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Tim Holloway wrote:. . . . The Babylonians knew what they were doing using 60 for a base. . . ..

We used to use 60×4=240 as a base.
 
Perry Graham
Greenhorn
Posts: 13
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I finally figured out how to fix my code, well it's probably not optimized completely but I'm still practicing with the Math class.

Campbell Ritchie wrote:Use integer arithmetic for the loop header.


This was the first thing I worked on.

I knew that the total amount of x1 values is equal to twice the radius when the radius is multiplied by ten. And then I had to figure out how to do it when the increment value isn't something with 1. Well the answer to that is you just have to divide the first part by the increment multiplied by 10. Then you have to use Math.ceil and then cast to int to round up to the next integer. This got me:

The next part I did was to first set the x1 value initially as the radius outside of the for-loop, then to get the correct values (and not the -0.00 problem) in the loop I subtracted the increment value from it, then multiplied it by 100.0. From there I used the Math.round to get it to a nice usable value, then divided by 100.0 to get back to the desired decimal place.

I also added an if-statement at the beginning of the for-loop just in case my loopInt was off by a couple of values, the output would still be correct.

 
Campbell Ritchie
Marshal
Posts: 69874
278
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Perry Graham wrote: . . .

Campbell Ritchie wrote:Use integer arithmetic for the loop header.

. . . . This got me:

That looks horribly complicated. There has to be a more straightforward way to do that, and things straightforward are less likely to go wrong. What about 1 + (int)(radius / increment * 2)?

. . . . not the -0.00 problem

You are not calculating -0.00. You are calculating something small and negative, and then rounding it to two dp with %9.2f. It must be the 1.3877787807814457E-16. If you look in the documentation for BigDecimal, you will find that the nearest double representation of 0.1 is very slightly too large, but it you add it to itself 10× the rounding error goes the other way and you end up with 0.9999999999... That is how the loop I showed you yesterday runs 11×

in the loop I subtracted the increment value from it, then multiplied it by 100.0. From there I used the Math.round to get it to a nice usable value, then divided by 100.0 to get back to the desired decimal place.

That looks unnecessarily complicated again. You should be able to calculate values for the loop variant such that you can use i * 0.01. That will introduce rounding errors, but at least the errors won't be cumulative, and when you multiply by 0 you will actually get 0.0.

I also added an if-statement at the beginning of the for-loop just in case my loopInt was off by a couple of values . . .

It would be better to calculate the likely values of the loop variant on paper first. Work out a formula, and you can be confident it will be correct. As it is, if there are any rounding errors, you can be pretty confident that your if will miss the equality you are looking for.
Please explain what the variables x1, y1, x2, and y2 mean. Are you trying to convert polar coordinates to rectangular coordinates (=Cartesian coordinates)? That is where some explanatory comments would be useful. It is also (partially) what the methods hypot(), atan2(), sin(), and cos() are there for in the Math class. Assuming you have coordinates in the form (rθ), you calculate y from rsin(θ) and x from rcos(θ) as Piet showed you last night. Remember θ is measured in radians. Don't try arithmetic with θ×π÷180 or similar because Math has ready‑made methods to interconvert radians and degrees.

I don't know whether Math#pow() has the optimisation Tim mentioned for small argument values. You would have to explore the source code. But you can calculate Pythagoras/polar coordinates without using Math#pow.
 
Piet Souris
Bartender
Posts: 4006
156
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
You can ease that increment problem a little. If you have a point (x, y) in the first quadrant, then (-x, y) is in the second quadrant, (-x, -y) in the third and (x, -y) in the fourth. So you need to consider your intervals only from 0 to R, and not -R to R.

The next code snippet is what I use sometimes. It is pretty general, it divides any segment into equal length segments, based on an initial stepsize. It means that that stepsize may be adjusted slightly, and that may or may not be a probem.
 
Campbell Ritchie
Marshal
Posts: 69874
278
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
...and what Piet showed is a concrete example of using integer arithmetic for the looping and calculating a double inside the loop from those integers.
 
Paul Clapham
Marshal
Posts: 25682
69
Eclipse IDE Firefox Browser MySQL Database
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
If the requirement is to produce the output rounded to two decimal places, you could consider using BigDecimal values. That might produce its own surprises, but maybe it's worth trying.
 
Perry Graham
Greenhorn
Posts: 13
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

That looks horribly complicated. There has to be a more straightforward way to do that, and things straightforward are less likely to go wrong. What about 1 + (int)(radius / increment * 2)?


Using this actually fixed the problem of the loop sometimes going over the expected end of the negative x1 value causing a NaN for the two y values, I guess simplicity is key!

To show the problem that got fixed, using a radius 0f 0.09 and increment of 0.01 with my original got me

   Points on a Circle of Radius 0.09

    x1       y1       x1       y2
--------------------------------------
   0.09     0.00     0.09     0.00
   0.08     0.04     0.08    -0.04
   0.07     0.06     0.07    -0.06
   0.06     0.07     0.06    -0.07
   0.05     0.07     0.05    -0.07
   0.04     0.08     0.04    -0.08
   0.03     0.08     0.03    -0.08
   0.02     0.09     0.02    -0.09
   0.01     0.09     0.01    -0.09
   0.00     0.09     0.00    -0.09
  -0.01     0.09    -0.01    -0.09
  -0.02     0.09    -0.02    -0.09
  -0.03     0.08    -0.03    -0.08
  -0.04     0.08    -0.04    -0.08
  -0.05     0.07    -0.05    -0.07
  -0.06     0.07    -0.06    -0.07
  -0.07     0.06    -0.07    -0.06
  -0.08     0.04    -0.08    -0.04
  -0.09     0.00    -0.09     0.00
  -0.10      NaN    -0.10      NaN
  -0.11      NaN    -0.11      NaN
  -0.12      NaN    -0.12      NaN
  -0.13      NaN    -0.13      NaN
  -0.14      NaN    -0.14      NaN
  -0.15      NaN    -0.15      NaN
  -0.16      NaN    -0.16      NaN
  -0.17      NaN    -0.17      NaN
  -0.18      NaN    -0.18      NaN


While using the same radius and increment with Campbell's suggestion of got me

   Points on a Circle of Radius 0.09

    x1       y1       x1       y2
--------------------------------------
   0.09     0.00     0.09     0.00
   0.08     0.04     0.08    -0.04
   0.07     0.06     0.07    -0.06
   0.06     0.07     0.06    -0.07
   0.05     0.07     0.05    -0.07
   0.04     0.08     0.04    -0.08
   0.03     0.08     0.03    -0.08
   0.02     0.09     0.02    -0.09
   0.01     0.09     0.01    -0.09
   0.00     0.09     0.00    -0.09
  -0.01     0.09    -0.01    -0.09
  -0.02     0.09    -0.02    -0.09
  -0.03     0.08    -0.03    -0.08
  -0.04     0.08    -0.04    -0.08
  -0.05     0.07    -0.05    -0.07
  -0.06     0.07    -0.06    -0.07
  -0.07     0.06    -0.07    -0.06
  -0.08     0.04    -0.08    -0.04
  -0.09     0.00    -0.09     0.00
 
Perry Graham
Greenhorn
Posts: 13
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Also for anybody interested, attached is a jpg of my assignment instructions.
Assignment-Instructions.jpg
[Thumbnail for Assignment-Instructions.jpg]
 
No more fooling around. Read this tiny ad:
Building a Better World in your Backyard by Paul Wheaton and Shawn Klassen-Koop
https://coderanch.com/wiki/718759/books/Building-World-Backyard-Paul-Wheaton
    Bookmark Topic Watch Topic
  • New Topic