1

I'm messing around with Fourier transformations. Now I've created a class that does an implementation of the DFT (not doing anything like FFT atm). This is the implementation I've used:

public static Complex[] Dft(double[] data)
    {
        int length = data.Length;
        Complex[] result = new Complex[length];

        for (int k = 1; k <= length; k++)
        {
            Complex c = Complex.Zero;
            for (int n = 1; n <= length; n++)
            {
                c += Complex.FromPolarCoordinates(data[n-1], (-2 * Math.PI * n * k) / length);
            }
            result[k-1] =  1 / Math.Sqrt(length) * c;
        }
        return result;
    }

And these are the results I get from Dft({2,3,4})

alt text

Well it seems pretty okay, since those are the values I expect. There is only one thing I find confusing. And it all has to do with the rounding of doubles.

First of all, why are the first two numbers not exactly the same (0,8660..443 8 ) vs (0,8660..443). And why can't it calculate a zero, where you'd expect it. I know 2.8E-15 is pretty close to zero, but well it's not.

Anyone know how these, marginal, errors occur and if I can and want to do something about it.

It might seem that there's not a real problem, because it's just small errors. However, how do you deal with these rounding errors if you're for example comparing 2 values.

5,2 + 0i != 5,1961524 + i2.828107*10^-15

Cheers

mtrw
  • 34,200
  • 7
  • 63
  • 71
Timo Willemsen
  • 8,717
  • 9
  • 51
  • 82
  • True true, however, if I want to compare these values it will say they're not the same. How would you deal with this. – Timo Willemsen Jan 04 '11 at 21:09
  • 3
    Read this: http://docs.sun.com/source/806-3568/ncg_goldberg.html and understand all of it before you write another line of code. – Eric Lippert Jan 04 '11 at 21:26
  • @Eric, I've read the introduction and read through it quickly, it seems to explain very nicely what I'm trying to understand. It's printing out atm, so I have a nice read for tonight xD – Timo Willemsen Jan 04 '11 at 21:35

3 Answers3

6

I think you've already explained it to yourself - limited precision means limited precision. End of story.

If you want to clean up the results, you can do some rounding of your own to a more reasonable number of siginificant digits - then your zeros will show up where you want them.

To answer the question raised by your comment, don't try to compare floating point numbers directly - use a range:

if (Math.Abs(float1 - float2) < 0.001) {
  // they're the same!
}

The comp.lang.c FAQ has a lot of questions & answers about floating point, which you might be interested in reading.

Carl Norum
  • 219,201
  • 40
  • 422
  • 469
  • +1 for the range idea. When working with doubles and other numeric types that exhibit precision loss, this is pretty much the best you can do. – NotMe Jan 04 '11 at 21:24
  • 3
    Note that this kind of thing can lead to problems of its own. For example, suppose there are three, float1, float2 and float3. You can easily get into a situation where your code reports that float1 is "the same" as float2, and float2 is "the same" as float3, but float1 is not the same as float3. That is a bizarre situation to be in; many algorithms such as sorting algorithms make the assumption that things equal to the same are equal to each other; be extremely careful when you're coming up with implementations of "equality" that are not equivalence relations. – Eric Lippert Jan 04 '11 at 21:46
4

From http://support.microsoft.com/kb/125056

Emphasis mine.

There are many situations in which precision, rounding, and accuracy in floating-point calculations can work to generate results that are surprising to the programmer. There are four general rules that should be followed:

  1. In a calculation involving both single and double precision, the result will not usually be any more accurate than single precision. If double precision is required, be certain all terms in the calculation, including constants, are specified in double precision.

  2. Never assume that a simple numeric value is accurately represented in the computer. Most floating-point values can't be precisely represented as a finite binary value. For example .1 is .0001100110011... in binary (it repeats forever), so it can't be represented with complete accuracy on a computer using binary arithmetic, which includes all PCs.

  3. Never assume that the result is accurate to the last decimal place. There are always small differences between the "true" answer and what can be calculated with the finite precision of any floating point processing unit.

  4. Never compare two floating-point values to see if they are equal or not- equal. This is a corollary to rule 3. There are almost always going to be small differences between numbers that "should" be equal. Instead, always check to see if the numbers are nearly equal. In other words, check to see if the difference between them is very small or insignificant.


Note that although I referenced a microsoft document, this is not a windows problem. It's a problem with using binary and is in the CPU itself.

And, as a second side note, I tend to use the Decimal datatype instead of double: See this related SO question: decimal vs double! - Which one should I use and when?

Community
  • 1
  • 1
NotMe
  • 87,343
  • 27
  • 171
  • 245
  • Awesome, I'll use that. However, I can't use the decimal datatype because I can't do System.Math and System.Numerics.Complex calculations with it. – Timo Willemsen Jan 04 '11 at 21:22
  • 3
    @Timo: a good rule of thumb is to always use decimal for *financial* calculations. For *scientific* caluclations use double and learn to live with small inaccuracies. – Eric Lippert Jan 04 '11 at 21:44
1

In C# you'll want to use the 'decimal' type, not double for accuracy with decimal points.

As to the 'why'... repsensenting fractions in different base systems gives different answers. For example 1/3 in a base 10 system is 0.33333 recurring, but in a base 3 system is 0.1.

The double is a binary value, at base 2. When converting to base 10 decimal you can expect to have these rounding errors.

Simon
  • 413
  • 5
  • 14
  • The reason why I chose for the double type, is that the whole Math library and the System.Numerics (complex type) uses the double to calculate these things. I couldn't find a library that could work with doubles, so I guess I'd have to write one myself then, if I want to use decimals. – Timo Willemsen Jan 04 '11 at 21:18
  • @Timo Willemsen: The other potential tact to take is to set the precision of your doubles to an acceptable level. Basically, decide at what point rounding will work for you. – NotMe Jan 04 '11 at 21:23
  • 1
    Decimals are not infinite precision either; they also exhibit rounding error for exactly the same reason you call out. You can't express 1/3 exactly in decimal either. That will also get rounded, and rounding error will accrue just the same. The set of values and operations that accrue error in decimal is *different* than the set for double, but it is still *large*. – Eric Lippert Jan 04 '11 at 23:59
  • @Eric: Isn't there a way to have no rounding errors, something that preserves values, for instance if the value is 1/3 like you said, then multiplying it with 3 again will make it 1 again, that sort of thing. Something that knows more about the value and preserves it. Not sure if that makes sense. – Joan Venge Jan 05 '11 at 01:39
  • 1
    @Joan: Yes. You could represent fractions as *two BigIntegers*, one for the numerator and one for the denominator. With that technique you can represent any number that is a fraction perfectly; of course, with this scheme you cannot exactly represent irrational numbers like pi or the square root of 2. – Eric Lippert Jan 05 '11 at 14:49
  • 1
    @Joan: You're welcome. A fairly common exercise to give students learning a language is to implement a "Rational" type that can do all the arithmetic operations on rationals implemented as a pair of integers. It's a good refresher in basic number theory as well. – Eric Lippert Jan 05 '11 at 18:23
  • Thanks Eric. That makes sense. I wasn't aware of that kind of assignment as I didn't take any courses. – Joan Venge Jan 05 '11 at 19:12