15

I write the following c++ program in CodeBlocks, and the result was 9183. again I write it in Eclipse and after run, it returned 9220. Both use MinGW. The correct result is 9183. What's wrong with this code? Thanks. source code:

#include <iostream>
#include <set>
#include <cmath>

int main()
{
   using namespace std;
   set<double> set_1;
   for(int a = 2; a <= 100; a++)
   {
       for(int b = 2; b <= 100; b++)
       {
           set_1.insert(pow(double(a), b));
       }
   }
    cout << set_1.size();

return 0;
}
Sam
  • 485
  • 2
  • 8
  • 17
  • 14
    My guess: You are running into floating point precision errors. – Chad Nov 29 '12 at 22:03
  • 4
    I don’t agree with the downvotes on this question. However, the relevant point here (and, I suspect, the reason for the downvotes) is that you did not post the command line arguments used by your IDEs for the compilation. Those might make a difference. IDEs themselves are just vehicles for different tools, they’re largely irrelevant. – Konrad Rudolph Nov 29 '12 at 22:04
  • I'm guessing one's compiling in 32bit mode and the other in 64bit, getting slightly different results. – Mat Nov 29 '12 at 22:06
  • 3
    If this is the reason for the downvotes, then they are worthless without comment, since the OP cannot improve his question. – Olaf Dietsche Nov 29 '12 at 22:06
  • 1
    You can print intermediary results to see the difference. – SChepurin Nov 29 '12 at 22:19
  • What compiler options are being used by each IDE? Assuming it's g++ if one passes in `-ffast-math` or similar you could easily get different results. Also, how do you know which result is correct (rather than just what you expected)? – Mark B Nov 29 '12 at 22:26
  • I don't understand, the statement `cout << set_1.size()` is printing the size of the set, which has nothing to do with the type of the data in the container. The `set::size` method returns the number of items in the container. See http://www.cplusplus.com/reference/set/set/size/ – Thomas Matthews Nov 29 '12 at 23:26

4 Answers4

11

You are probably seeing precision errors due to CodeBlocks compiling in 32-bit mode and Eclipse compiling in 64-bit mode:

$ g++ -m32 test.cpp
$ ./a.out
9183
$ g++ -m64 test.cpp
$ ./a.out
9220
Justin ᚅᚔᚈᚄᚒᚔ
  • 15,081
  • 7
  • 52
  • 64
  • That comparison is wrong. The margin will be too large if `a` and `b` are small, and too small if `a` and `b` are large. – Dietrich Epp Nov 29 '12 at 22:23
  • 1
    @DietrichEpp: You may be right; since I don't have sufficient knowledge of floating-point comparison in C++ I'm going to just remove the equation and leave it as an exercise for the reader to find the correct way to use doubles as a key in a `set` *(hint: don't)*. – Justin ᚅᚔᚈᚄᚒᚔ Nov 29 '12 at 22:27
  • 3
    It's not necessarily wrong to compare floating-point numbers with `==`, and there are a number of cases in which doing so is correct. The catch is that computing the same number in two different ways may give slightly different results, e.g., `exp(2.0)` versus `square(exp(1.0))`, and different implementations will also give different results. So it *could* be fine to use `set`, but only if you really know what you're doing. – Dietrich Epp Nov 29 '12 at 22:34
  • 2
    For posterity: [Relevant discussion](http://stackoverflow.com/q/6684573/265575) about floats as keys in a set. – Justin ᚅᚔᚈᚄᚒᚔ Nov 29 '12 at 22:36
  • @Justinᚅᚔᚈᚄᚒᚔ - well, maybe relevant, but mostly fear and uncertainty. – Pete Becker Nov 29 '12 at 23:10
  • What does the size of the container have to do with doubles, floats or chars? – Thomas Matthews Nov 29 '12 at 23:28
  • @ThomasMatthews: In my original answer I had included a criticism of using `float` and `double` as keys in an associative container, and included a suggestion to utilize epsilon in a custom comparator for the container. However, since I don't currently possess sufficient expertise on this topic to suggest guidelines for others, I removed that part of the answer -- which unfortunately rendered Dietrich's first comment a bit obsolete. If you click on the link where it says "edited (date)", you'll see the revision history and the original comparison that Dietrich was referencing in his comment. – Justin ᚅᚔᚈᚄᚒᚔ Dec 04 '12 at 19:19
3

If I cast both arguments to double I get what you would expect:

pow(static_cast<double>(a), static_cast<double>(b))
RicLeal
  • 923
  • 9
  • 23
3

The difference appears to be due to whether the floating point operations are using 53-bit precision or 64-bit precision. If you add the following two lines in front of the loop (assuming Intel architecture), it will use 53-bit precision and give the 9220 result when compiled as a 32-bit application:

uint16_t precision = 0x27f;
asm("fldcw %0" : : "m" (*&precision));

It is bits 8 and 9 of the FPU that control this precision. The above sets those two bits to 10. Setting them to 11 results in 64-bit precision. And, just for completeness, if you set the bits to 00 (value 0x7f), the size is printed as 9230.

Mark Wilkins
  • 40,729
  • 5
  • 57
  • 110
2

Actually you're not really supposed to rely on == (or technically, x <= y && y <= x) for doubles anyway. So this code produces implementation-dependent results (not strictly speaking UB, per comments, but what I meant :) )

djechlin
  • 59,258
  • 35
  • 162
  • 290
  • 5
    Its not undefined behavior -- comparisons on doubles can't crash or corrupt memory -- but it is implementation defined how much precision there is on these operations, so one compiler might produce different results from another. – Chris Dodd Nov 29 '12 at 22:10
  • I can't understand what your answer mean. I copied code from one IDE and paste it in the other one. – Sam Nov 29 '12 at 22:11
  • 2
    Compiler is free to do some rounding on double values. `4` might be stored as something like `3.000019`, and `2` as `1.99998`, so `2.0*2.0 != 4.0`. But if the compiler is precise in these cases, as it's allowed to be, they will come out as equal. This is the sort of thing you're relying on in your sets. – djechlin Nov 29 '12 at 22:13
  • Since both IDEs use MinGW, I would expect the same result as well, be it either 9183 or 9220. – Olaf Dietsche Nov 29 '12 at 22:15
  • Do they both use gcc? They clearly are producing different assembled code - compare the outputs at each stage of compilation. – djechlin Nov 29 '12 at 22:16
  • 2
    @djechlin Nah, the compiler isn’t at all “free to do” that. They compiler is forced to abide by the relevant IEEE standard which specifies these behaviours very precisely. There is quite little leeway. (What a compiler *can* do, though, is ignore this behaviour when reordering expressions to assume that this reordering doesn’t have observable effects.) – Konrad Rudolph Nov 29 '12 at 22:19