Techblog

Floorplanner's adventures in foam

Month: March 2008

Rounding errors in practice

In college I followed a course in numerical analysis. The main point of the course was to be careful with floating point arithmetic, because it is vulnerable to rounding errors that can significantly influence the result of complex computations. Until yesterday I never had encountered such a problem. Now that I have lost my innocence in this matter, I would like to share my tale of nasty debugging and frustration.

After receiving some bug reports of Floorplanner designs that failed to save properly, we dove into the code to see what was going wrong. After some time, we found that the errors were caused by the script that loads the design after it has been saved with a unique name. This unique name is passed to the script to be able to find the design. We used the current timestamp as a unique name for the design. The current timestamp simply is the number of seconds passed since January 1, 1970 and looks something like this: 1206712028. As a design name, this number was passed to different scripts, both client-side and server-side. However, at some point in this chain of scripts, the number was changed slightly to 1206712030 and because of this the associated design could not be found, resulting in an error.

At first, we investigated the possibility that the stored timestamp was overwritten by a newer timestamp, as this could explain the slight increase in the number. However, we were not able to find this anywhere in the code and sometimes, the number was decreased a bit instead of being increased.

Finally, we monitored the data being sent between the different scripts, and we found that ActionScript automatically converted the numeric design name into a number in scientific notation. In our case, this would be 0.1206712028 x 10^10. Unfortunately, this number was rounded to 0.120671203 x 10^10 because computers use floating point arithmetic to store numbers in scientific notation. This number would eventually be converted back to normal notation, but it was now 1206712030 because of the rounding error.

We fixed it by putting an ‘a’ in front of the timestamp, preventing the automatic conversion to a number. Not very elegant, but it works!

Encoding UTF-8 in Javascript

I had to make a call from Javascript to a SOAP web service (more on that later). The data I had to send to the web service was in a XML format that contained non ASCII characters (for example: é). I tried to use the standard escape() function, but that one couldn’t handle the special characters.

Thanks to ecmanaut I found a very simple solution: encodeURIComponent() (with decodeURIComponent() as its counterpart).

Putting HTTP status codes to use with Rails


Note: I have released a Rails plugin that creates an exception class for every HTTP status to your Rails application and adds a default handler for these exceptions, based on the examples in this post.

We are currently implementing an API for Floorplanner, so other sites can use the service Floorplanner offers for their own needs. The Floorplanner website is developed with Rails, so we are trying to be a good Rails citizen and create a API based on REST.

REST embraces the HTTP protocol by coupling URIs to resources and HTTP methods GET, PUT, POST and DELETE) to actions for manipulating them. Today, I tried to embrace the HTTP protocol even more by using its various status codes for Floorplanner’s particular needs.

Most people will know the HTTP 404 status code, which a server returns if you request a page that does not exist. But there are many more interesting codes that can be put to good use as well. The Forbidden status code (403) can for instance be returned if you try to access a another user’s floorplan. Moreover, Floorplanner offers paid subscriptions that include additional functionality. If you try to access this functionality with an account without the necessary privileges, an Upgrade Required status code (426) can be returned. And if you forgot to pay your subscription fee, a Payment Required code (402) can be returned to indicate this.

However, in the end, users wants to see a nice and informative page that tells them what is wrong without cryptic error codes. This is easily possible by leveraging some new features in Rails 2. First of all, we define some custom exceptions that can be raised if some of these conditions occur is wrong:

class PermissionDenied 
Now, we can raise these exceptions when needed in our controllers:
def show
  @plan = Floorplan.find(params[:id])
  raise PermissionDenied unless @plan.user == current_user
  # ...
end

def login
  # ...
  raise AccountExpired if current_user.account_expired?
  # ...
end

Normally, raised exceptions will trigger an Internal server error (HTTP status code 500). The new exceptions will be handled manually to return the intended status code and serve a pretty page explaining what is wrong. Exceptions can be caught using the rescue_from method. If we put the code in the Application controller, it will automagically work for all our controllers. DRY.

class ApplicationController  "shared/status_#{status.to_s}", :status => status }
      format.any  { head status } # only return the status code
    end
  end

end

Now, it is easy to create super fancy pages by editing the views file like app/views/shared/status_forbidden.html.erb.

Note that you cannot and should not use this method for handling internal server errors with status code 500. These should not occur because we have thought of every possible way our code will be used and misused… in theory. In practice, we installed the exception notifier plugin, so we receive a message if one of these occurs and get our asses back to work.

© 2014 Techblog

Theme by Anders NorenUp ↑