Sunday, April 4, 2010

Perl and The Time It Owes Me

These days I'm working on a my final Verilog project, where I create a simulator for a basic Verilog syntax (netlist only) in Verilog.

The first stage in creating such simulator is parsing the input .v file into two data structures that contain the netlist and the list of cells in the file. This code was given to us by the instructor, and it's written in Perl.

To gather more knowledge about our design, I've decided to create a Python Verilog simulator, similar in concepts to the one we're going to create and verify that it's behaving like the well-known software simulator that we're working with.

After fixing some small bugs, I've noticed that my code generates some events in one time-unit less than the real simulator. After checking my code again and verifying that no bugs lie there I checked the tables that were created by the Perl script that my instructor wrote, and there it was: one line of a bad parsed data.

The parser takes a line that looks like "bla bla bla 1.974 bla bla bla", parses it using a Regular Expression and converts the number to a floating point number (which is always in the format d.ddd) to integer by multiplying it by 1000 and then formatting it using sprintf() (casting to int using the int() function yields the same results).

Looks fine, ain't it? That's what I thought too, but it seems that there's a floating point error that causes a lot of trouble for specific numbers, like 1.007. Then after conversion you get 1006 and some temporary madness.

If you don't believe me, check the following code:

$num = 1.007;
print "Floating number: $num\n";

$newNum = $num * 1000;
print "Possibly integer: $newNum\n";

print sprintf("Real integer: %d\n", $newNum);

Say it's a bug, say it's a feature, I don't care.

This was tested under Perl 5.10.0 under Linux and Windows.

Update: to clear this out, the problem lies in rounding of floating point numbers, and probably exists in every implementation that uses the IEEE representation format. The solution as I see it right now is to use string formatting "%.0f" to convert the floating point variable to string with rounding and not truncation (that's what casting to integer does) and then convert it from string to integer.

Thanks goes to lorg and Michael for clarifying the real facts.

Update 2: it seems that this "feature" doesn't apply to C, because these numbers can be represented in the regular IEEE standards. Now it's a bit weird to me that Perl and Python act exactly the same (any suggestions?).