aspose file tools*
The moose likes Beginning Java and the fly likes Java can't do arithmetic? Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login


Win a copy of Spring in Action this week in the Spring forum!
JavaRanch » Java Forums » Java » Beginning Java
Bookmark "Java can Watch "Java can New topic
Author

Java can't do arithmetic?

Chris Crawford
Ranch Hand

Joined: Jun 22, 2011
Posts: 87

I have no idea how I might find this problem in the archives, so I'll just have to present it here. This is the craziest, screwiest problem I've ever seen. I have a rather complicated program. In accordance with what I think are Java protocols, I have completely separated input functions from graphics functions from computational functions. In other words, when the user clicks the mouse, the mouse handlers do not carry out any graphics functions; they just change a few global variables and call repaint(). The paint() method is the only one that is allowed to draw onto the screen. I'm not using components: just a JFrame with a lot of Graphics2D methods.

Here's the most egregious example of my problem:

This is a debugging statement, and under semi-repeatable circumstances it will print out:

phase error! 0 0

"Now just a gol-durn moment!" I said to myself upon seeing this. I checked my calculation on my fingers. " Yep, 0 is not greater than 0+1. What gives?"

Then followed a long series of checks, double checks, variations, and so on. I found a number of other manifestations of the basic Java-can't-do-arithmetic bug. I restarted the computer and opened only Eclipse (I'm using Eclipse on Mac OS Lion). Same problem. OK, maybe my version of Eclipse is corrupted, so I downloaded the latest version of Eclipse and tried again. No love. I have continued to fiddle around with it, learning such things as:

1. There are actually several different errors of this type that appear when I exactly replicate the circumstances.
2. These errors always occur at exactly the same point in execution, but WHICH error appears seems random.
3. The errors never appear early in program execution; they always appear after at least 10 calls to paint(). Sounds like a heap problem, doesn't it?
4. The errors are not exclusively in response to a single set of error conditions. I have gotten errors on different combinations of values of phase and previousPhase. There does seem to be a pattern, but I can't quite nail it down.
5. Sometimes the error doesn't strike; I cruise through the standard test sequence with nary a complaint.
6. I have, for test purposes, eliminated some of the larger data structures. These are still quite small -- less than a kilobyte each -- but doing so does permit the program to last longer before hitting the bug. Again, this suggests a heap problem, but I just don't have any big data structures; the biggest private data structure is a 3D array of doubles, with just six elements in each dimension; I have three instances of it.

I have run these tests hundreds of times now and I just can't figure out what's wrong. I have triple-checked to insure that there are no new Graphics or Graphics2D objects being created or duplicated. During program initialization, there is a single g = getGraphics; and g2 = (Graphics2D) g; After that, those two values are never changed.

I still haven't checked all the crazy possibilities that might underly this problem; there are zillions of crazy possibilities, but I can't figure out any reasonable explanations. Can anybody suggest a possibility, preferably reasonable (but I'll take crazy), for why I'm getting this behavior?

Jeff Verdegan
Bartender

Joined: Jan 03, 2004
Posts: 6109
    
    6

The only possible way that code produces that output is if phase and/or previousPhase are member variables and at least one of them is being changed in another thread between the if and the println. A more likely case is that that's not actually the code you're running. Maybe you changed something and didn't recomopile. Maybe you have a semicolon after the if line, so that there's no real "if" about it and the print is executing every time.

Note that you should always use braces with your if statements, even when not necessary. Change those two lines to this, recompile, and see what happens.
Jeff Verdegan
Bartender

Joined: Jan 03, 2004
Posts: 6109
    
    6

Chris Crawford wrote:I found a number of other manifestations of the basic Java-can't-do-arithmetic bug.


Given all the machinations you went through, you're probably aware of this and the above is probably hyperbole, but let me just throw this out there to be sure. Which is more likely: that a programming language that has been around for nearly 20 years and that is used in thousands or maybe even millions of production applications "can't do basic arithmetic"; or that you're misunderstanding something or making an incorrect assumption?

Believe me though, I feel your pain. We've all been there. "This can't possibly be happening, but it is!"
Rob Rowan
Greenhorn

Joined: Nov 14, 2012
Posts: 11
Looks to me you're not using "{" after the if parameters. You're using "(" or "))" instead. Make sure you open a new block of code when you create an if statement.
Jeff Verdegan
Bartender

Joined: Jan 03, 2004
Posts: 6109
    
    6

Rob Rowan wrote:Looks to me you're not using "{" after the if parameters. You're using "(" or "))" instead. Make sure you open a new block of code when you create an if statement.


He's not using () instead. The outer () are required for the if condition. The inner ones are not replacing the braces, which would go around the println(). They're just enforcing an operator precedence in the if test, albeit unnecessarily.

The code he posted:


will do exactly what he's saying it should do, and not what he's saying it is doing (barring the potential multithreading issue I mentioned). While it is good practice to always use braces with if statements, the lack of them in the above is most definitely NOT the cause of the observed undesired behavior.
Rob Rowan
Greenhorn

Joined: Nov 14, 2012
Posts: 11
Oh! I've never encountered that. Thanks for clearing that up! I'm new at this! Just came on here looking for help and looking if I can help anyone else.
Jeff Verdegan
Bartender

Joined: Jan 03, 2004
Posts: 6109
    
    6

It's great that you want to help others. However, before posting an answer, if you research to make sure it's correct, that will not only prevent confusion for those reading the thread, it will also help you to learn along the way! I've been programming with Java for about 14 years, and answering questions on Java forums for about 13, and I still end up learning stuff in the course of research I do to answer questions.
Chris Crawford
Ranch Hand

Joined: Jun 22, 2011
Posts: 87

Thanks for the suggestions. Yes, I'm not serious about blaming Java for what is obviously pilot error; I was just trying to express the bug in the simplest terms.

I went through using curly braces after the IF-statement; no joy.

Just after I posted the first message, I decided that the best thing to do would be to step away from the problem and let it simmer in my mind. Sure enough, halfway through lunch, an answer popped into my head: the AffineTransform! I have a short routine that draws rotated triangles, using an AffineTransform and a Polygon. I had properly dealt with the Polygon by invalidating it immediately after I was done with it, but I couldn't recall whether I had disposed of the AffineTransform. As soon as I got back to the computer, I checked and sure enough, the AffineTransform is created but never disposed of. What a stupid mistake!

But then the plot thickened; the AffineTransform class has no means of disposal. You can't use dispose() or invalidate, and I can't find anything in the class definition that allows me to destroy the object once it has been created. Arg!

But I was not to be defeated by something so silly. I made the AffineTransform a global, created it just once in the initialization phase, and then altered its properties every time I needed to use it. Ha!

It didn't work. So I got down and dirty: I did a global search for every single instance of the keyword "new". That would give away anything. The MouseListener created a Rectangle to test the location of the cursor; I globalized it. Still no luck.

I found a place where I added a new BasicStroke. That got moved to initialization, too. Still no luck. I ranged through the entire program, replacing every single instance in which I created a new object with a globalized version in which the object was created just once and then had its properties manipulated at each usage. I have double-checked now, and there is nothing anywhere that creates any new object other than the initialized objects (created just once at the outset of the program) without also disposing of it. That heap has to be stable now!

But the bug persists! I'm looking around for a handy noose, vial of poison, or pistol.



Jeff Verdegan
Bartender

Joined: Jan 03, 2004
Posts: 6109
    
    6

I'm not familiar with the specific classes you mentioned, and I don't know much at all about GUIs. Additionally, you didn't show your code with the changes I suggested or state what the exact results were when you applied them. So all I can say at this point is, contrary to my initial guess, it seems like you have a multithreading bug. (Sorry about that--I went all tl;dr on your first post and didn't realize you were dealing with Swing.) The fix for that will involve one or more of synchronization, volatile variables, java.util.concurrent, and immutable objects. Without an SSCCE, however, it's nigh impossible to give any concrete insight to what your bugs are.
Chris Crawford
Ranch Hand

Joined: Jun 22, 2011
Posts: 87

Yes, I've pretty much ruled out heap problems. Moreover, this problem first appeared while I was in the process of segregating input from output from processing, which certainly suggests some sort of thread collision. So let me first make certain of my assumption: is it a rule of Java that all changes in the display be made through the paint() method? This is, should MouseListeners merely change program variables that are read by paint()? To put the same question a third way, is it permissible to do graphics manipulations (say, g2.drawString("Hello", 100, 100)) in a MouseListener or anywhere else in the program?

Steve Luke
Bartender

Joined: Jan 28, 2003
Posts: 4181
    
  21

Chris Crawford wrote:...I ranged through the entire program, replacing every single instance in which I created a new object with a globalized version in which the object was created just once and then had its properties manipulated at each usage....


You have gone the complete wrong direction. Every instance you make should be a method local variable, unless there is an absolute reason to make it live longer. What about all those objects? They go out of scope and can get garbage collected. And if everything is a method local variable there is pretty small chance that you get threading issues.

Chris Crawford wrote: is it a rule of Java that all changes in the display be made through the paint() method? This is, should MouseListeners merely change program variables that are read by paint()? To put the same question a third way, is it permissible to do graphics manipulations (say, g2.drawString("Hello", 100, 100)) in a MouseListener or anywhere else in the program?


The graphics object should never escape the paint/paintComponent method, you should never hold that in any scope outside of method local (that is an absolutism so I am sure it is wrong and there will be cases when it isn't true, but most cases it is true). Other methods should only store variables which tell the paintComponent() what to do. And those values should only be set inside the Event Dispatch Thread. When in doubt, use SwingUtilities to make sure the code that sets values that paint relies on gets executed on the EDT.


Steve
Chris Crawford
Ranch Hand

Joined: Jun 22, 2011
Posts: 87

Thanks, Steve. Inasmuch as globalizing all those objects didn't accomplish the purpose, I'll go back and localize them all again. Oof -- more slogging. I am going to have to be more careful in the way that I keep those threads apart. Fortunately, the way the program works, it should be possible to isolate the threads so that no two are active at the same time. That might fix this problem once and for all.
Jayesh A Lalwani
Bartender

Joined: Jan 17, 2008
Posts: 2402
    
  28

Wait.. so you do have multiple threads?! That'll mess you up big time if you don't have synchronization. Why exactly do you have threads?
Chris Crawford
Ranch Hand

Joined: Jun 22, 2011
Posts: 87

Maybe I don't understand threading, but I have three basic sets of functions:

1. the mainline program, which does all the basic computing
2. the MouseListener, which reads the mouse and alters global variables that are then used by both the mainline program and the graphics display
3. The paint() method, which (I assume) operates on its own thread.

I'm using a JFrame and drawing Graphics2D stuff into it. I've gone to some lengths to segregate activity: whenever the mainline program wants mouse input, it waits until a global flag is set by the MouseListener indicating that a proper input has been accepted.

I have a very bad feeling that I've gotten something profoundly wrong here. Nothing I try seems to work. I threw away all of today's work and reverted to the program as it was yesterday, hoping to avoid any screwups from today, but the crazy behavior just won't go away. I am going to spend tomorrow working on something completely different, letting things simmer, and then on Friday I'll set out with grim determination to at least come up with a precise specification of what I'm doing, so that you folks can have something specific to look at. I may even write a tiny demo program that contains the essence of the structure I'm using now.

I greatly appreciate the effort you fellows have gone to trying to help me.
Campbell Ritchie
Sheriff

Joined: Oct 13, 2005
Posts: 39409
    
  28
If you have frames or other Swing components, you must access them all in the same thread. You should use a thread created specially for the purpose, called the event despatch thread (EDT) or similar. There are more details here. You should start the EDT like this (or similar):-There are lots of other ways to start the EDT. Note you are using an anonymous class; if you use the args parameter inside that class, it must be marked final.
You can start other threads, eg with a SwingWorker object, but do not attempt to access any Swing components from other threads. You can get nasty errors because (as you will see from the link above) Swing is not thread safe.
Jayesh A Lalwani
Bartender

Joined: Jan 17, 2008
Posts: 2402
    
  28

If you have variables that are being updated in your main thread, that are being used in your Listeners/Paint method, then you have a possibility that the variable will be changed while you are using it in Listeners and Paint method. My suggestion is that. The issue is thread safety not artihmetic. It's not just Swing that is thread unsafe. Your entire implementation is thread unsafe

a) either understand threading concepts and redesign your application
b) Do not do anything outside of Listener and Paint method. Listeners and Paint method are called from a thread called EventDispatcherThread. Putting code only in the listeners/paint ensures that your code isn't multi threaded.. as long as you aren't using SwingWorkers and/or starting other threads
Chris Crawford
Ranch Hand

Joined: Jun 22, 2011
Posts: 87

I've spent the last day reading up on threading and musing over the general structure of the program, and I now understand the fundamental flaw in the structure I set up. I'll rework the program so that there are two threads instead of three: mouse listening plus reaction processing, and painting. I really can't understand why I messed this up in such a fashion; I've always used this structure in the past. I suspect my mistake arose because I was working outside of conventional Swing and was drawing directly in Graphics2D. In any case, I think I'm on the right track now.

I'd again like to thank all of you for setting this old fossil straight. If you ever need any help with FORTRAN or 6502 assembler, I'll be happy to share my vast and utterly obsolete expertise in these useless topics.
Jeff Verdegan
Bartender

Joined: Jan 03, 2004
Posts: 6109
    
    6

Chris Crawford wrote:...6502 assembler...


Whoa! There's a blast from the past! My first exposure to assembly language was on the 6502 on an Apple ][e.
 
I agree. Here's the link: http://aspose.com/file-tools
 
subject: Java can't do arithmetic?