-3

Recently I decided to get into c++, and after going through the basics I decided to build a calculator using only iostream (just to challenge myself). After most of it was complete, I came across an issue with my loop for exponents. Whenever a multiple of Pi was used as the exponent, it looped way too many times. I fixed it in a somewhat redundant way and now I'm hoping someone might be able to tell me what happened. My unfixed code snippet is below. Ignore everything above and just look at the last bit of fully functioning code. All I was wondering was why values of pi would throw off the loop so much. Thanks.

bool TestForDecimal(double Num) /* Checks if the number given is whole or not */ { 
    if (Num > -INT_MAX && Num < INT_MAX && Num == (int)Num) {
        return 0;
    }
    else {
        return 1;
    }
}

And then heres where it all goes wrong (Denominator is set to a value of 1)

if (TestForDecimal(Power) == 1) /* Checks if its decimal or not */ {
    while (TestForDecimal(Power) == 1) {
        Power = Power * 10;
        Denominator = Denominator * 10;
    }
}

If anyone could give me an explanation that would be great!

To clarify further, the while loop kept looping even after Power became a whole number (This only happened when Power was equal to a multiple of pi such as 3.1415 or 6.2830 etc.)

Heres a complete code you can try:

#include <iostream>

bool TestForDecimal(double Num) /* Checks if the number given is whole or not */ {
if (Num > -INT_MAX && Num < INT_MAX && Num == (int)Num) {
    return 0;
}
else {
    return 1;
}
}

void foo(double Power) {
double x = Power;
if (TestForDecimal(x) == 1) /* Checks if its decimal or not */ {
    while (TestForDecimal(x) == 1) {
        x = x * 10;
        std::cout << x << std::endl; 
    }
}
}

int main() {
foo(3.145); // Substitute this with 3.1415 and it doesn't work (this was my problem)
system("Pause");
return 0;
}
Jacobardio
  • 31
  • 1
  • 1
  • 6
  • 2
    You seem to be taking a roundabout way to check whether something is a decimal. Does this help at all? http://stackoverflow.com/questions/9612839/is-there-a-way-to-check-if-a-variable-is-a-whole-number-c It's possible that the problem is simply poor floating point accuracy. If you divide a number by, say, 1000, and then add that number 1000 times, you're unlikely to arrive back at the original number because each calculation introduces tiny errors. – Luke May 12 '15 at 06:08
  • Related question: http://stackoverflow.com/questions/30135668/testing-if-a-number-has-a-decimal-value-using-only-the-iostream-library – Tony Delroy May 12 '15 at 06:13
  • @Luke: Even if it has poor floating point accuracy, It shouldn't make the function loop any more once Power becomes a whole number. – Jacobardio May 12 '15 at 06:13
  • Num == (int)Num The behaviour of that cast may be undefined. I'm really not sure if this is the best way of checking for whole numbers. Why not std::floor(x) == x? – Luke May 12 '15 at 06:16
  • @Luke, assuming the value <= 1 when the for loop finishes executing, perhaps ``(int)`` isn't quite so bad, but I do agree. – Patrick Roberts May 12 '15 at 06:18
  • Also, just a note that you're returning int literals when the function says it'll return doubles, and then comparing that double to a 1. Looks odd. Also, how does you function behave if you pass a negative number...? :O – Luke May 12 '15 at 06:19
  • Considering that dips into the cmath header, I needed some other method that works with only iostream. – Jacobardio May 12 '15 at 06:21
  • None of your code makes any sense. That is no way to 'test for decimal' and it is no way to exponentiate either. There's no reason why any of this should work at all. – user207421 May 12 '15 at 06:21
  • Should I include the entire calculator? – Jacobardio May 12 '15 at 06:22
  • @Luke The behaviour of `(int)Num` is only undefined if the value is not in `int`'s range, but the code checks that. –  May 12 '15 at 06:40

5 Answers5

1

What's wrong with doing something like this?

#include <cmath> // abs and round
#include <cfloat> // DBL_EPSILON

bool TestForDecimal(double Num) {
  double diff = abs(round(Num) - Num);
  // true if not a whole number
  return diff > DBL_EPSILON;
}
Patrick Roberts
  • 49,224
  • 10
  • 102
  • 153
  • There's absolutely no problem with that, I just wanted to write my calculator using only iostream for the extra challenge it would present. :) – Jacobardio May 12 '15 at 06:17
0

The look is quite inefficient...what if Num is large...

A faster way could be something like

if (Num == static_cast<int>(Num))

or

 if (Num == (int)Num)

if you prefer a C-style syntax.

Then a range check may be useful... it oes not make sense to ask if Num is an intger when is larger than 2^32 (about 4 billions)

Finally do not think od these numers as decimals. They are stored as binary numbers, instead of multiplying Power and Denominator by 2 you are better of multiplying them by 2.

marom
  • 5,064
  • 10
  • 14
0

After studying your "unfixed" function, from what I can tell, here's your basic algorithm:

  1. double TestForDecimal(double Num) { ...

A function that accepts a double and returns a double. This would make sense if the returned value was the decimal value, but since that's not the case, perhaps you meant to use bool?

  1. while (Num > 1) { make it less }

While there is nothing inherently wrong with this, it doesn't really address negative numbers with large magnitudes, so you'll run into problems there.

  1. if (Num > -INT_MAX && Num < INT_MAX && Num == (int)Num) { return 0; }

This means that if Num is within the signed integer range and its integer typecast is equal to itself, return a 0 typecasted to a double. This means you don't care whether numbers outside the integer range are whole numbers or not. To fix this, change the condition to if (Num == (long)Num) since sizeof(long) == sizeof(double).

Perhaps the algorithm your function follows that I've just explained might shed some light on your problem.

Patrick Roberts
  • 49,224
  • 10
  • 102
  • 153
0

Beware. A double is (generally but I think you use a standard architecture) represented in IEE-754 format, that is mantissa * 2exponent. For a double, you have 53 bits for the mantissa part, one for the sign and 10 for the exponent. When you multiply it by 10 it will grow, and will get an integer value as soon as exponent will be greater than 53.

Unfortunately, unless you have a 64 bits system, an 53 bits integer cannot be represented as a 32 bits int, and your test will fail again.

So if you have a 32 bits system, you will never reach an integer value. You will more likely reach an infinity representation and stay there ...

The only use case where it could work, would be if you started with a number that can be represented with a small number of negative power of 2, for example 0.5 (1/2), 0.25(1/4), 0.75(1/2 + 1/4), giving almost all digits of mantissa part being 0.

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
0

Most decimal fractions can't be represented exactly in a binary floating-point format, so what you're trying to do can't work in general. For example, with a standard 64-bit double format, the closest representable value to 3.1415 is more like 3.1415000000000001812.

If you need to represent decimal fractions exactly, then you'll need a non-standard type. Boost.Multiprecision has some decimal types, and there's a proposal to add decimal types to the standard library; some implementations may have experimental support for this.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • How do examples such as foo(3.142347) work then? Pardon my confusion, just not fully understanding. – Jacobardio May 12 '15 at 06:59
  • @Jacobardio: That example [doesn't work for me](http://ideone.com/W5x3JS). Your other example of `3.145` appears to work by accident, because it's close enough to its nearest representable value that rounding errors cancel out the difference. – Mike Seymour May 12 '15 at 07:09
  • Would there be any way to fix this? – Jacobardio May 12 '15 at 07:12
  • @Jacobardio: Yes, use a type that can represent decimal fractions exactly, like I said. – Mike Seymour May 12 '15 at 07:13