Έχουν δημοσιευτεί Δευτέρα, 30 Μαρτίου 2009 1:08 πμ από το μέλος Markos

Hybrid Arithmetic Types. How to increase computational accuracy and range in a DIY approach.

In the process of developing software that has to deal with complex numerical computations, many times you have to think of ways to manipulate your biggest enemy: the double type. Some may disagree, but for a certain category of mathematical problems this type causes more trouble than convenience. Its accuracy may seem sufficient but it is inadequate. Round off errors accumulate and the final result is meaningless. Its range may seem large enough, but we quite often end up with an overflow or an underflow condition. What can be done then? Is there anything else to use in its place?

The decimal type seems to be a promising candidate. Well… it’s not!! Neither its numerical range is wider than that of the double type nor its accuracy is always exploitable. Suppose we have to multiply two numbers that differ greatly in their order of magnitude, for example 2 times srtq[2]e-25; the smaller one will be truncated to such an extent so that their product will be completely inaccurate. OK, the decimal type by itself can’t do much. But, what if it is combined with another type? Say, an int or a long?

Decimal’s 28 digit accuracy makes it a perfect mantissa. The only other thing we need, is an integer exponent and this way we’ve just created our hybrid type (which is of course an object). The tricky part is to force the mantissa’s absolute value to range between 1 (inclusive) and 10 (exclusive) or to be equal to 0. The next step is to define methods for addition, subtraction, multiplication and division or to overload the appropriate operators.

The implementation is as easy as it sounds. The way to perform basic arithmetic operations is already defined and well documented. For addition and subtraction we must first scale the exponents and then add or subtract the mantissae. Multiplication and division is much more straightforward since we only have to multiply (divide) mantissae and add (subtract) the exponents. When everything is finished we must scale the result so that we end up with a new hybrid type that will meet the specifications already mentioned in the previous paragraph.

Are we done yet? Oh, NO!! We also need a library to calculate basic mathematical functions like Exp(x) and Sin(x), etc. The most straightforward approach to do this is to use MacLaurin series. Surely, there are better and more efficient ways to achieve convergence, but for now MacLaurin series will do just fine.

 

The library

I have to confess that this is not the original implementation of the library. My first attempt was made a few years ago while i was learning C#. It had several drawbacks and very bad performance. The reason for this was that at the time i was experimenting with the string approach as well, in order to achieve “user-defined” floating point accuracy. Strings were involved a little more than they should be (a lot more actually) and because of that i didn’t use operator overloading. Anyway, at the time i had no idea where the code would lead me.

This library is different. The code is a lot clearer and more readable. However, i cannot claim it is bug free. Should you find any bugs i would appreciate it a lot if you bring them to my attention. The object that makes all of the above possible is called HybridDecimal and the class that is used to calculate basic mathematical functions is named HybridMath.

Although HybridDecimal is an object, it has to behave like a numeric type. So a lot of overloading was needed to achieve that kind of behavior. It cooperates smoothly with other numeric types in almost every aspect, but assignment. For instance, you can write:

HybridDecimal a = new HybridDecimal(1.3234, 234); a=a * 123;

But you can’t write: a = 1; You should write a = (HybridDecimal)1; instead.

Another aspect you should keep in mind is that HybridDecimal is a strange beast. The type of the exponent is long, so it is practically impossible to suffer from an overflow or underflow condition. I know that because i tested the original library extensively. Whenever a numerical procedure took a wrong path, it never exited. So in this implementation i had to add an OverflowExponent property to simulate the overflow – underflow condition.

Please try it and let me know of what you think.

Share



Attachment(s): HybridDecimal.zip

Σχόλια:

 

Dimitris Papadimitriou έγραψε:

This is very interesting!

There is something you can do to avoid casting on assignment. You need to use implicit operator. Have a look: http://msdn.microsoft.com/en-us/library/z5z9kes2(VS.71).aspx

Μαρτίου 30, 2009 10:00 μμ
 

Markos έγραψε:

There are a few numeric types that are not included in the provided code. For example, float was left out. Everyone can modify it freely acording to his/her needs (DIY). Another modification might be to create a struct instead of a class. In fact, the use of a struct might be more appropriate. I leave all those decisions to the user. I only wanted to present the idea.

Μαρτίου 30, 2009 10:25 μμ