28

Consider the following piece of code:

#include <iostream>
#include <cmath>

int main() {
    int i = 23;
    int j = 1;
    int base = 10;
    int k = 2;
    i += j * pow(base, k);
    std::cout << i << std::endl;
}

It outputs "122" instead of "123". Is it a bug in g++ 4.7.2 (MinGW, Windows XP)?

user1257
  • 622
  • 1
  • 8
  • 13
  • 14
    I feel like this is a good time for the old [What Every Computer Scientist Should Know About Floating-Point Arithmetic](http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html) reference. – Daniel Kamil Kozar Apr 06 '13 at 13:50
  • +1, But i wonder how have you come to this sample code to get this strange result in this only environment? – SChepurin Apr 06 '13 at 13:52
  • Anyhow, you should qualify `pow` with `std::` or do a using declaration. The code you showed is not portable since a C++ implementation is not required to provice any pow function in the global scope. It's possible that it solves your problem. It if does, see my answer for an explanation. – sellibitze Apr 06 '13 at 14:23
  • 2
    I was doing exercise §6.6[16] from Bjarne Stroustrup's book on C++ that says to write a function that converts string representation of integers to integer. I gave the function input 123 and it returned 122 so I started to investigate the issue. – user1257 Apr 06 '13 at 14:26
  • I suspect it may be violating the IEEE 754 standard, as all the intermediate results are small integers and therefore exactly representable, though `pow()` is only listed as a "recommended correctly rounded" function in the standard, so I'm not sure. Hopefully someone more familiar with the precise language of the standards can clarify? – hammar Apr 06 '13 at 14:32
  • 2
    Your example would work with a correctly rounded `pow()` floating-point function. You can find one here: http://lipforge.ens-lyon.fr/www/crlibm/ . However, your approach would still show its limits with computations involving larger integers, which may not be represented exactly as floating-point numbers (and then there is nothing even a correctly rounded `pow()` function can do). – Pascal Cuoq Apr 06 '13 at 14:36
  • 1
    @sellibitze: Qualifying `pow` with `std::` gives 122 as well. – user1257 Apr 06 '13 at 14:50
  • @hammar “recommended” means that it is not a requirement for `pow()` to be correctly rounded. However, for this question's inputs, the result is exactly representable as a floating-point number (either single- or double- precision), meaning that the OP's `pow()` function is wrong by more than 1ULP. This is rather bad. – Pascal Cuoq Apr 06 '13 at 14:50
  • 1
    If I declare `double i = 23;` the result is correct. – user1257 Apr 06 '13 at 16:07
  • 7
    @DanielKamilKozar This article is not relevant to the question, since it only discusses the function `PositivePower(x,n)` (function which would make the OP's example work if it were used). The article does **not** discuss the general `pow()`, nor why it should be wrong by more than 1ULP. You have a good heuristic with “Cite Goldberg every-time something strange happens with floating-point”, but it is only a heuristic. – Pascal Cuoq Apr 06 '13 at 20:07
  • @PascalCuoq: Are you sure crlibm's `pow` is correctly rounded? When I look at the CVS log, I see the comment "New version of power (not completely correctly rounded)." (Staring at the code, I see that it'll get cases like this one right. I'm just questioning your assertion that it provides a correctly-rounded `pow`.) – tmyklebu Apr 07 '13 at 01:07
  • @tmyklebu If I remember correctly, the article says that some functions are only “correctly rounded with astronomical confidence”. Without looking more into it I would expect that `pow()` is in that category. Since writing that comment, I have myself realized that one only needs a “faithful” function `pow()` (their vocable for a function that returns a result within 1ULP of the mathematical value). Crlibm's `pow()` is certainly faithful. – Pascal Cuoq Apr 07 '13 at 01:18
  • @tmyklebu Indeed, `pow()` is a difficult beast. The details are page 159 in http://ftp11.tw.freebsd.org/FreeBSD/ports/distfiles/crlibm/crlibm-1.0beta3.pdf – Pascal Cuoq Apr 07 '13 at 01:32
  • If you want the square of an integer `n`, just write `n*n`. `pow()` is overkill for the purpose (as well as being unreliable, as you've seen). – Keith Thompson Aug 16 '15 at 03:52
  • @KeithThompson I wanted to raise one integer to the power of another integer. I wrote my own function to do that. – user1257 Aug 17 '15 at 07:21

5 Answers5

12

std::pow() works with floating point numbers, which do not have infinite precision, and probably the implementation of the Standard Library you are using implements pow() in a (poor) way that makes this lack of infinite precision become relevant.

However, you could easily define your own version that works with integers. In C++11, you can even make it constexpr (so that the result could be computed at compile-time when possible):

constexpr int int_pow(int b, int e)
{
    return (e == 0) ? 1 : b * int_pow(b, e - 1);
}

Here is a live example.


Tail-recursive form (credits to Dan Nissenbaum):

constexpr int int_pow(int b, int e, int res = 1)
{
    return (e == 0) ? res : int_pow(b, e - 1, b * res);
}
Community
  • 1
  • 1
Andy Prowl
  • 124,023
  • 23
  • 387
  • 451
  • 5
    But normally, you would expect floating point arithmetic to work just fine when both the inputs and output are (smallish) integers. (1.0 + 1.0 == 2.0, with no rounding errors, for example). So I can't blame the OP for being puzzled at rounding errors being introduced in *this* particular case (that is, `pow(10.0, 2.0) != 100.0`) – jalf Apr 06 '13 at 13:54
  • And now, for brownie points, make it tail recursive (yes, the compiler most likely will figure this out by itself but still – this is one optimisation I would *not* rely on). – Konrad Rudolph Apr 06 '13 at 14:00
  • std::pow is overloaded. There are overloads that take integers. I think the problem is that the OP used an unqualified pow function. The C++ standard does not quarantee the global scope to contain all these overloads let alone a single pow function. – sellibitze Apr 06 '13 at 14:12
  • @KonradRudolph: Is that OK? – Andy Prowl Apr 06 '13 at 14:12
  • @jalf Worse, OP’s code uses only integer declarations, it’s highly unusual (and definitely a *mistake* in the type system) that C++ silently performs a lossy conversion (`float` -> `int`). – Konrad Rudolph Apr 06 '13 at 14:12
  • @sellibitze: I am not aware of overloads working on integers. The standard does not mention them, neither does the reference I linked – Andy Prowl Apr 06 '13 at 14:26
  • @AndyProwl: I meant to say "integers as exponents". If you don't have an integer as 2nd parameter, you obviously cannot implement something like your function or the square-and-multiply algorithm but have to rely on something like `exp(log(base)*exponent)`. – sellibitze Apr 06 '13 at 14:31
  • @sellibitze: OK, but even that is working on floating-point values, so how does it solve the OP's problem? – Andy Prowl Apr 06 '13 at 14:33
  • @AndyProwl The tail-recursive form always needs to pass the current (intermediate) result into the next call (typically via the 3rd parameter). A corrected version is here: http://liveworkspace.org/code/2gGk5A$0 – Dan Nissenbaum Apr 06 '13 at 14:48
  • @DanNissenbaum: But I am passing the current result, just in the first parameter rather than in the 3rd. Is that wrong? – Andy Prowl Apr 06 '13 at 14:52
  • 3
    -1 just for bringing up the naive `int_pow` implementation. Learn the basics [here](https://en.wikipedia.org/wiki/Exponentiation_by_squaring). – Stéphane Gimenez Apr 06 '13 at 14:55
  • @AndyProwl As it stands, the current result in that particular snippet is always `b`, because `n` is always `1`. – Dan Nissenbaum Apr 06 '13 at 14:56
  • @DanNissenbaum: Correct. I don't know how I could overlook that. Thank you – Andy Prowl Apr 06 '13 at 15:02
  • 1
    Who cares about efficiency in this case? Everything is done compile-time. Also, e will be at most 64 in practice. – Petter Apr 06 '13 at 16:09
  • 7
    This answer fails to identify the core problem. It states “std::pow() works with floating point numbers, which do not have infinite precision,” but lack of precision is not the reason that `pow` returns the wrong result. There is more than enough precision in the parameters and the return type of `pow` to return an exact result. The actual problem is a poor quality `pow` implementation. A good `pow` implementation will return exactly 1024 for `pow(10., 2.)`. Blaming floating-point arithmetic for this is like blaming integer arithmetic if `45 % 7` returned 2. – Eric Postpischil Apr 06 '13 at 20:55
  • Ugh, wrote 1024 when I meant 100. I mentally swapped the arguments of `pow(10., 2)`, which is obviously 100. – Eric Postpischil Apr 06 '13 at 21:01
  • @EricPostpischil There's no way for a function that returns a floating point quantity to return *exactly* an integer value. It will always return something either within a small delta above, or a small delta below, the exact integer. Due to the rounding rules of C++ that require that a cast from a floating point number to an integer always round down (rather than round to nearest), the ~50% of the time that the `pow()` function returns a small delta below the exact value, the C++ standard makes clear that that value will be rounded down. This is expected even for a good `pow()` implementation. – Dan Nissenbaum Apr 06 '13 at 22:35
  • 4
    @DanNissenbaum: Per IEEE Standard for Floating-Point Arithmetic (754-2008), clause 3.3, floating-point formats represent (-1)**s•b**e•m, where s is 0 or 1, e is an integer in an interval depending on the format, and m is a number represented by a certain digit string depending on the format (equivalent to an integer in a certain range divided by a certain power of the base), plus +infinity, -infinity, quiet NaN, and signaling NaN. **Nothing** in the standard says floating-point objects represent intervals. That is incorrect common lore passed by rumor, not specification. – Eric Postpischil Apr 06 '13 at 22:57
  • @DanNissenbaum: Furthermore, it is **not** expect that a good `pow` implementation will return a result that is “randomly” above or below the value of a result that is exactly representable. When a result is exactly representable in the format, according to the IEEE 754 standard, a good `pow` implementation returns exactly that result. – Eric Postpischil Apr 06 '13 at 23:00
  • 3
    @DanNissenbaum: That is incorrect. Floating-point numbers are able to represent exact integers within a range determined by the number of bits in the mantissa, and a correctly-rounded `pow()` will return exact results for integers within that range. – hammar Apr 06 '13 at 23:02
  • IEEE 754-2008 clause 9.2: “Recommended correctly rounded functions … pow …”. Clause 2.1.12: “correct rounding: This standard’s method of converting an infinitely precise result to a floating-point number, as determined by applicable rounding direction. A floating-point number so obtained is said to be correctly rounded.” Clause 4.3: “Rounding takes a number regarded as infinitely precise and, **if necessary** [emphasis mine], modifies it to fit in the destination’s format…”. – Eric Postpischil Apr 06 '13 at 23:04
  • @hammar However, a "correctly-rounded" `pow()` function is an oxymoron. There's no way for the runtime system to know that the user *wants* to round to the nearest integer. The rounding rules of C++, however, do make clear that casts to integers must round down. – Dan Nissenbaum Apr 06 '13 at 23:10
  • 1
    @DanNissenbaum: “Correctly-rounded `pow`” is fully defined by the IEEE 754 standard. In binary floating-point with round-to-nearest, ties-to-even, mode, it is the unique value you would get by calculating the infinitely precise mathematical result and then choosing the nearest representable value, with various provisos on domain and overflow and such that do not apply in this case. In this case, the runtime system “knows” the user wants to round to the nearest integer because the result **is** an integer. – Eric Postpischil Apr 06 '13 at 23:16
  • @EricPostpischil I'm assuming that the `pow()` function in question returns a floating-point quantity. The runtime system performs a cast on this quantity after the `pow()` function has returned. – Dan Nissenbaum Apr 06 '13 at 23:18
  • 1
    @DanNissenbaum: ... which should also not require any rounding in this case, since the floating-point quantity represents an integer well within the bounds of the type it's being cast to. – hammar Apr 06 '13 at 23:21
  • @hammar I believe it would be the cast from `float` to `int` that is handled in a way that knows how to interpret the `float` result in case of ambiguity, rather than the `pow()` function being the issue here (assuming the `pow()` function is doing its job). – Dan Nissenbaum Apr 07 '13 at 00:46
  • Also, interestingly, assuming that modern compilers *do* perform the `float`-to-`int` conversion in favor of the exact integer whenever the floating-point value is consistent with an exact integer, it would lead to bias in scientific calculations were that integer cast back to a `float` and used in a calculation. – Dan Nissenbaum Apr 07 '13 at 00:57
  • 3
    @DanNissenbaum: Please, please read the things Eric Postpischil said. 100 can be exactly represented in any standard floating-point type. A decent `pow` should at least get the exact cases right, of which `pow(10., 2.)` is one. Consequently, a `pow` that returns something other than 100 for `pow(10., 2.)` is a poor `pow`. `pow` returned the wrong result, and consequently rounding gave the wrong integer. This isn't rounding being broken. – tmyklebu Apr 07 '13 at 01:14
  • @tmyklebu I'm certainly not saying `pow(10., 2.)` is returning anything inconsistent with an exact value of 100. Clearly, that 100 lies within a range of potential floating-point values. It is reasonable for a cast to `int` to assume that any floating-point value that corresponds to an exact integer to make that cast; however, there is no way for the runtime system to know that a floating-point value that corresponds to an exact integer really is an exact integer. Hence, the behavior would lead to bias in the scenario I mention. – Dan Nissenbaum Apr 07 '13 at 01:23
  • 3
    @DanNissenbaum: I'm bothered by your use of the phrase "consistent with an exact value of 100." Floating-point numbers aren't fuzzy things where the computer does whatever it wants and you get screwed. They are a sign bit, a significand, and an exponent. Addition and subtraction and friends all have well-defined semantics. So does conversion to an integer. You'd do well to familiarise yourself with how they work. Your talk of "bias" and "no way for the runtime system to know that a floating-point value[...]is an exact integer." smacks of muddled ideas about how floating-point works. – tmyklebu Apr 07 '13 at 01:31
  • 3
    @DanNissenbaum: Specifically, `100.0` *is* an exact integer. It's not "consistent with" an integer or anything of the sort; it *is* an exact integer that just happens to be represented as a significand times 2exponent. A good `pow` damn well ought to return that value, `100.0`, when asked what `pow(10., 2.)` is. If it does, then the well-defined conversion to integer does its well-defined thing and you get exactly the integer `100`. – tmyklebu Apr 07 '13 at 01:33
  • @tmyklebu It's simply a fact that any floating point value represented on a computer, including IEEE 754, must by necessity represent a range of values given by some `delta`. For example, what if the true floating point value desired as the result of some computation was 122.999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 (exactly)? The floating-point representation of this number would be equivalent to the floating-point representation of the perfect integer value 123. – Dan Nissenbaum Apr 07 '13 at 01:36
  • 3
    @DanNissenbaum: No, a floating-point number represents exactly one value. They have no notion of the user's desires. Numbers within an interval around that floating-point number may get rounded to that floating-point number, but 123.0 is a representation for 123.0 and never for 123 - 2-1000 or anything of the sort. Like I said, you'd do well to learn how these things work. – tmyklebu Apr 07 '13 at 01:39
  • @tmyklebu The fact that a floating-point value has been rounded is exactly why the system, which does not know the user's intent (as you agree), does not know if the original exact value before rounding was, in fact, an exact integer. Any such imposition (as in a cast to integer that assumes this) will introduce bias. Certainly, casting to an integer (and back to a float) would cause bias in a sufficiently sensitive scientific calculation. This is why the issue is related to the *cast*, not to the `pow()` function. – Dan Nissenbaum Apr 07 '13 at 01:49
  • 1
    @DanNissenbaum: The problem has nothing to do with a cast. The cast is downstream of the inaccuracy. Is `printf` buggy because `printf("%d\n", INT_MAX + INT_MAX);` prints out a negative number when I wanted it to print out a really big positive number? No; it did as it was ordered faithfully. In this case, I, the programmer, fed it gatbage. In the OP's case, `pow` fed the cast garbage by returning the wrong value. – tmyklebu Apr 07 '13 at 02:01
  • @tmyklebu If the `pow()` function had a bug, I agree. However, we don't know if the `pow()` function had a bug (unless someone knows that this particular CRT has a bug in the `pow()` function). But there's nothing I know of in the standard that requires this cast from `float` to `int` to interpret an ambiguous floating-point value one way or the other (i.e., to interpret a floating-point value consistent with an exact integer as *being* that exact integer), although I believe most/all common current compilers do (since it will not impact scientific calculations that never do a cast to `int`) – Dan Nissenbaum Apr 07 '13 at 02:11
  • 1
    @DanNissenbaum: What's an "ambiguous floating-point value"? Your thinking about floating-point numbers is ambiguous. You should really fix that. – tmyklebu Apr 07 '13 at 02:32
  • @tmyklebu Please see my example above, where two different potential desired results of a floating-point computation map to the identical floating-point IEEE 754 representation. – Dan Nissenbaum Apr 07 '13 at 02:35
  • @DanNissenbaum: What makes your 123.0 ambiguous and my 123.0 unambiguous? How's the implementation supposed to tell whether it's got my 123.0 or your 123.0 when all 64 bits it's allowed to look at are taken up by 123.0? – tmyklebu Apr 07 '13 at 02:40
  • @tmyklebu When you see my `123.0`, how do you know that my intended value was not `129.9999999...`? Of course, you can't know that. It is not the case that the `123.0` "wins". Instead, the `123.0` (or any floating-point representation) is *always* an approximation representing a range `delta` of possible numbers. Where this matters - in sensitive scientific calculations where it is the *intended* value that is desired, not the value represented by the IEEE 754 representation - sophisticated analysis must be undertaken to determine the inaccuracy in the result due to the FPU error. – Dan Nissenbaum Apr 07 '13 at 02:47
  • 5
    @DanNissenbaum: Why are you still spouting this confusion? I told you before and you refuse to listen; the implementation doesn't care what you intend. It's not in the mind-reading business. It sees 123.0 and knows that it would be insane to round 123.0 to 122. Like I said before, floating-point numbers are not necessarily approximations and they don't represent ranges of real numbers. Each one represents a single real number. Modern FPUs don't make errors. Please, please, please learn how floating-point works. – tmyklebu Apr 07 '13 at 02:55
  • @tmyklebu We agree that the implementation is not in the mind-reading business. We also agree that floating-point numbers are not necessarily approximations. Also, we agree that they don't represent ranges of real numbers and that each one represents a single real number. However, what you've left off is that ranges of real numbers correspond to a single floating-point number, and this does lead to bias if the **user** assumes that an integer resulting from a cast implies that the **user's** desired value was exactly an integer. – Dan Nissenbaum Apr 07 '13 at 03:03
  • 4
    @DanNissenbaum: No, dammit. `pow` is the problem. It is illegal for an implementation to convert `100.0` to anything other than 100. It's obviously giving 99, which means that **`pow` did not return `100.0`.** – tmyklebu Apr 07 '13 at 03:06
  • @tmyklebu I disagree. It's not obvious to me that `pow()` is giving `99`. What makes you say that? I assume it's giving `100.0` (up to its maximum precision). – Dan Nissenbaum Apr 07 '13 at 03:12
  • @DanNissenbaum: `pow` does not need to give `99`. It only needs to give something at least `99.0` and less than `100.0.` Which it evidently is. – tmyklebu Apr 07 '13 at 03:47
  • @tmyklebu Unless there's a bug with `pow()` (which I doubt), then `pow()` is returning exactly `100.0` (within the defined floating-point precision). – Dan Nissenbaum Apr 07 '13 at 03:49
  • 2
    @DanNissenbaum: Why do you doubt that the OP's system `pow` is broken? It obviously is. Why are you assuming the complicated thing (`pow`) is completely flawless (when it's an open research problem to find a completely flawless `pow`) and the simple thing (double-to-int conversion) behaves in these completely insane ways? Go learn about floating-point. There's an Internet full of useful resources. – tmyklebu Apr 07 '13 at 03:53
  • @tmyklebu It's certainly **not** obvious that `pow()` is broken. If it's IEEE-754 compliant, its answer will be exactly known to the bit. If it's not IEEE-754 compliant, then the definition of "flawless" is vague, but the result will in great likelihood be within a small (and typical) `delta` of `100`. I don't agree that `double`-to-`int` conversion is simple in such an edge case (hence the issue the OP is raising). – Dan Nissenbaum Apr 07 '13 at 03:58
  • 4
    @DanNissenbaum: It is insane to suggest that what integer `100.0` ought to be rounded to is an "edge case." No `pow` implementation that comes within 1 ulp of the correct result can possibly produce any value other than `100.0`. Go learn how floating-point numbers work from someone else; I'm sick of trying to force information on you when you obviously don't want to absorb it. – tmyklebu Apr 07 '13 at 04:09
  • @tmyklebu No, it's not insane to suggest that. It's how computers work. It's one of the reasons why there are two different data types to begin with. The ambiguity regarding how to interpret floating-point values that "appear" to be integers because the IEEE 754 representation is an integer, is especially revealed when contrasting the scenario of the OP's question against the scenario of a sensitive scientific calculation. Only in the former scenario is it "insane" to suggest that the floating-point "integer" does not necessarily represent an integer. By the way - your personal jabs are silly. – Dan Nissenbaum Apr 07 '13 at 04:19
  • @DanNissenbaum: It is true that all numbers in an interval are rounded to one floating-point number, and that information about the original result is lost because it is not present in the floating-point number. However, in spite of this, the **rules** about floating-point arithmetic are completely mechanical and completely defined; they do not make allowances for user intent or providing fudging of further results because past results were rounded. A correctly rounded `pow` function returns 100. for `pow(10., 2.)`, and a conversion of 100. to `int` produces 100 and never anything else. – Eric Postpischil Apr 08 '13 at 00:40
  • @DanNissenbaum @tmyklebu: I've updated my post, please take a look. Looks like the problem is with cast from `double` to `int`. – user1257 Apr 08 '13 at 11:12
  • @user1257: No, it's really not. The problem is with your `pow`. You didn't print `d` with enough precision. Try using `printf("%a\n", d);` instead. – tmyklebu Apr 08 '13 at 18:36
  • @tmyklebu: It outputs `0xc.8p+3`. Could you explain that? – user1257 Apr 08 '13 at 21:10
  • @tmyklebu: Skimming through http://en.wikipedia.org/wiki/Hexadecimal it looks like `0xc.8p+3` in hexadecimal notation means `100` in decimal notation. – user1257 Apr 09 '13 at 09:12
  • 1
    @user1257: For fun try "int i = d;" instead of int i = std::pow(10, 2);" Do both now print 100? – Rick Regan Apr 09 '13 at 13:21
  • 1
    @RickRegan: Yes, they do. So, why does `int i = std::pow(10, 2);` produce 99? Is it something with cast? – user1257 Apr 09 '13 at 14:25
  • 1
    @user1257: No, I think the problem is with pow() as others have said. I'm guessing your program is compiling into x87 FPU instructions (fadd, fmul, etc.) (Can you look at the assembler?) In this case the same double value (pow(10, 2)) can be computed to two different levels of precision (double and extended). (Read my article http://www.exploringbinary.com/when-doubles-dont-behave-like-doubles/ for background.) If pow(10, 2) was exactly 100, you wouldn't see two different printed values in your program. – Rick Regan Apr 09 '13 at 15:16
  • 1
    @user1257: Are you perhaps on x86? Look at the generated assembly code and see whether it produced a call to `pow`, then a `fistp` instruction or similar. That would indicate that `pow`'s result was an 80-bit `long double` that got converted to `int`. (Which is a well-known `gcc` "bug" that they refuse to fix because the performance cost is prohibitive.) – tmyklebu Apr 09 '13 at 15:16
7

All the other answers so far miss or dance around the one and only problem in the question:

The pow in your C++ implementation is poor quality. It returns an inaccurate answer when there is no need to.

Get a better C++ implementation, or at least replace the math functions in it. The one pointed to by Pascal Cuoq is good.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • 1
    So, you think that relying on the QoI is perfectly fine? I would not accept his code in a review because of the rounding issues. – sellibitze Apr 06 '13 at 22:14
  • 2
    @sellibitze: I wrote nothing about relying on the quality of implementation. Engineering consists of, among other things, deriving results from specifications. If you have a trusted specification that your library returns exact results when they are representable, then you may rely on it. If you do not have such a specification, then you may not rely on it. – Eric Postpischil Apr 06 '13 at 23:06
  • Sure, you're suggesting to replace the pow implementation instead of fixing the code. – sellibitze Apr 07 '13 at 11:31
  • Are you saying that the ISO C++ standard requires implementations of pow to return the precise result if it is expressible? If so: Can you point me to that guarantee? – sellibitze Apr 07 '13 at 12:15
  • @sellibitze: Re “replace the pow implementation instead of fixing the code”: Replacing the `pow` implementation is fixing the code. You seem to have some notion that you are stuck with whatever C++ implementation you happen to be using at the moment or are stuck with only the guarantees made by the C++ standard and no other. Neither is true. You are free to change or alter your C++ implementation, and you are free to secure guarantees other than those in the C++ standard. – Eric Postpischil Mar 28 '20 at 17:30
  • @sellibitze: Re “Are you saying that the ISO C++ standard requires…”: No, I wrote no such thing. – Eric Postpischil Mar 28 '20 at 17:31
3

Not with mine at least:

$ g++ --version | head -1
g++ (GCC) 4.7.2 20120921 (Red Hat 4.7.2-2)

$ ./a.out 
123

IDEone is also running version 4.7.2 and gives 123.


Signatures of pow() from http://www.cplusplus.com/reference/cmath/pow/

     double pow (      double base,      double exponent );
long double pow ( long double base, long double exponent );
      float pow (       float base,       float exponent );
     double pow (      double base,         int exponent );
long double pow ( long double base,         int exponent );

You should set double base = 10.0; and double i = 23.0.

Chris Seymour
  • 83,387
  • 30
  • 160
  • 202
  • I have windows version (MinGW). – user1257 Apr 06 '13 at 13:44
  • 6
    All you've shown is that there exists at least one version of GCC which behaves as the OP expected. That doesn't answer the question of whether the OP's version has a bug, or the behavior is permissible under the standard – jalf Apr 06 '13 at 13:50
  • 1
    @jalf Actually he's shown that it works in the *same* version as the OP (to the extent that the OP specified the version number). – JBentley Apr 06 '13 at 14:06
  • 1
    @JBentley he shows that it works with the same GCC version on a potential different CPU, OS, Standard library or GCC-build. what does that prove? – Johannes Schaub - litb Apr 06 '13 at 14:27
  • Actually, I could not find these overloads anymore in the C++11 specification. The overloads with integer exponents seem to have been removed. – sellibitze Apr 07 '13 at 12:04
3

Your problem is not a bug in gcc, that's absolutely certain. It may be a bug in the implementation of pow, but I think your problem is really simply the fact that you are using pow which gives an imprecise floating point result (because it is implemented as something like exp(power * log(base)); and log(base) is never going to be absolutely accurate [unless base is a power of e].

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
  • 1
    "unless base is a power of e" ... but you can't even represent e^n accurately as a floating point number, so I don't think "unless" is appropriate here, right? (EDIT: thinking about it, that doesn't invalidate your statement, it's just that it is simply impossible to have a base that is a power of e) – catchmeifyoutry Apr 06 '13 at 19:23
  • 4
    The result from pow in this question is not imprecise; it is inaccurate. The precision in in the return type is more than enough to return the exact value. – Eric Postpischil Apr 06 '13 at 21:04
  • I have edited so that is says "accurate" rather than "precise". – Mats Petersson Apr 11 '13 at 21:42
3

If you simply write

#include <iostream>
#include <cmath>

int main() {
    int i = 23;
    int j = 1;
    int base = 10;
    int k = 2;
    i += j * pow(base, k);
    std::cout << i << std::endl;
}

what do you think is pow supposed to refer to? The C++ standard does not even guarantee that after including cmath you'll have a pow function at global scope.

Keep in mind that all the overloads are at least in the std namespace. There is are pow functions that take an integer exponent and there are pow functions that take floating point exponents. It is quite possible that your C++ implementation only declares the C pow function at global scope. This function takes a floating point exponent. The thing is that this function is likely to have a couple of approximation and rounding errors. For example, one possible way of implementing that function is:

double pow(double base, double power)
{
    return exp(log(base)*power);
}

It's quite possible that pow(10.0,2.0) yields something like 99.99999999992543453265 due to rounding and approximation errors. Combined with the fact that floating point to integer conversion yields the number before the decimal point this explains your result of 122 because 99+3=122.

Try using an overload of pow which takes an integer exponent and/or do some proper rounding from float to int. The overload taking an integer exponent might give you the exact result for 10 to the 2nd power.

Edit:

As you pointed out, trying to use the std::pow(double,int) overload also seems to yield a value slightly less 100. I took the time to check the ISO standards and the libstdc++ implementation to see that starting with C++11 the overloads taking integer exponents have been dropped as a result of resolving defect report 550. Enabling C++0x/C++11 support actually removes the overloads in the libstdc++ implementation which could explain why you did not see any improvement.

Anyhow, it is probably a bad idea to rely on the accuracy of such a function especially if a conversion to integer is involved. A slight error towards zero will obviously make a big difference if you expect a floating point value that is an integer (like 100) and then convert it to an int-type value. So my suggestion would be write your own pow function that takes all integers or take special care with respect to the double->int conversion using your own round function so that a slight error torwards zero does not change the result.

sellibitze
  • 27,611
  • 3
  • 75
  • 95
  • 1
    Pretty sure the standard allows that `cmath` functions be defined in the global namespace. Yes, that sucks. – Konrad Rudolph Apr 06 '13 at 14:01
  • @KonradRudolph: I see no contradiction. The standard certainly does not require implementations to declare the C++ math functions at global scope. Having just a single pow function taking doubles from the C library available in this scope is one possible implementation effect. – sellibitze Apr 06 '13 at 14:03
  • 1
    Good `pow` implementations are not implemented with `exp(log(base)*power)`, and they are implemented well enough that `pow(10., 2.)` returns exactly 100. The problem here is not function resolution or floating-point arithmetic; it is solely quality-of-implementation. – Eric Postpischil Apr 06 '13 at 21:04
  • 1
    @EricPostpischil: "The problem is not function resolution" -- Well, it seems the "problem" is there because of a couple of reasons together. I personally would not expect a pow(double,double) function to be as accurate as possible. But I expect pow(double,int) to be implemented in a way that pow(10.0,2) exactly yields 100. Obviously, if pow(double,int) was implemented in that way and function resolution picked this function, the OP should have gotten 100 as result. But it seems that his std::pow(double,int) overload is not able to yield exactly 100.0. – sellibitze Apr 06 '13 at 22:07
  • @sellibitze: Regardless of whether you expect `pow(10., 2.)` to yield 100 or not, the fact that it does not is the cause of the observed behavior. – Eric Postpischil Apr 06 '13 at 23:08
  • @sellibitze: Where do you get `std::pow(double, int)` from? I do not see it in the C++ standard, draft n3092. – Eric Postpischil Apr 06 '13 at 23:18
  • 1
    @EricPostpischil: Seems to be in C++98, along with `std::pow(float, int)` and `std::pow(long double, int)`. See `lib.c.math`. – tmyklebu Apr 07 '13 at 01:20
  • It seems C++98 contained int overloads in whereas in C++11 they got removed. – sellibitze Apr 07 '13 at 12:03