Why does sqrt() work fine on an int variable if it is not defined for an int?
Asked Answered
B

6

7

In chapter 3 of Programming: Principles and Practice using C++ (sixth printing), Stroustrup states (p.68): "Note that sqrt() is not defined for an int".

Here is a simple C++ program based on that chapter:

#include "std_lib_facilities.h"

int main()
{
    int n = 3;
    cout << "Square root of n == " << sqrt(n) << "\n";
}

Given the quote above, I would expect the process of compiling or running this program to fail in some way.

To my surprise, compiling it (with g++ (GCC) 4.2.1) and running it succeeded without errors or warnings, and produced the following perfectly decent output:

Square root of n == 1.73205

My question therefore is: if sqrt() really is not defined for an int, then why doesn't the program above fail somehow?

Birthday answered 26/10, 2013 at 23:51 Comment(14)
You are doing what he said without knowing it. Your int is being implicitly converted to a double. I think he is recommending using a double explicitly, to avoid this kind of confusion.Burgoyne
Your software is doing an implicit conversion. As a guideline I always recommend avoiding implicit conversions - if a function takes a double, pass exactly a double to it, and nothing else.Eritrea
@Max Because I can't be bothered to explain implicit type conversions and function overloading in the level of detail needed to make what I think would be a good answer, or do the background checking to make sure I am correct about which conversions are happening. If someone else wants to spend that time, or post a half-arsed answer, that is up to them.Burgoyne
See section 3.9.1 "Safe Conversions", p. 79.Horsecar
Closely related discussion here: #5563500Horsecar
@AndreyT I don't think the dups answer is correct though, since there are multiple overloads and with an explicit case for integer values it would be ambiguous.Roseleeroselia
@Burgoyne there are three floating point overloads, without an explicit overload that covers the integer case it would be ambiguous.Roseleeroselia
@ShafikYaghmour Correct, sorry. I'm still in the habit of assuming people don't have C++ support - we are in a stagnant state of C++03 here :(Burgoyne
@Max And as it turned out, but not putting in the time to think about it carefully, I was wrong!Burgoyne
@BOBTFish Looks like an answer would have been better then, since then people could downvote it or you could edit it to be more correct.Carbolated
Thanks a lot for the help! Very good lesson for a beginner.Willemstad
@Shafik Yaghmour: You are right, but as you correctly noted in your answer the new spec intends to keep the legacy code valid, i.e. int arguments should "somehow" resolve to double overloads without triggering overload ambiguity error.Spermous
This question was merged with another exact duplicate, I updated my answer to be a bit more specific and as far as I can tell more correct as well.Roseleeroselia
Possible duplicate of sqrt() of int type in CSlapstick
R
5

Update 2

This question was merged with an exact duplicate, on taking a look at this, the actual answer is much simpler than anyone originally thought. The current version of std_lib_facilities.h includes the following line:

inline double sqrt(int x) { return sqrt(double(x)); }   // to match C++0x

which creates a specific overload for the int case to match what modern compilers should be be doing which is cast integer arguments to double, although this version does not cover all the cases.

If std_lib_facilities.h was not being used than the original logic still applies, although gcc-4.2 is rather old compared to Visual Studio 2012 from the original question but a 4.1.2 version have uses __builtin_sqrt specifically for the integer case.

Original

Since around 2005 the draft standard required integer argument to be cast to double this is covered in the draft C++ standard. If we look in section 26 Numerics library and then go to section 26.8 C library which covers the <cmath> header, it specifies overloads of the math functions for float, double and long double which is covered in paragraph 8:

In addition to the double versions of the math functions in , C++ adds float and long double overloaded versions of these functions, with the same semantics.

which would be ambiguous for the int case but the standard requires that sufficient overload are provided so that integer arguments are cast to double. It is covered in paragraph 11 which says(emphasis mine):

Moreover, there shall be additional overloads sufficient to ensure:

  1. If any arithmetic argument corresponding to a double parameter has type long double, then all arithmetic arguments corresponding to double parameters are effectively cast to long double.
  2. Otherwise, if any arithmetic argument corresponding to a double parameter has type double or an integer type, then all arithmetic arguments corresponding to double parameters are effectively cast to double.
  3. Otherwise, all arithmetic arguments corresponding to double parameters have type float.

Update

As @nos points out it is possible that the version of sqrt being called is from math.h header as opposed to the overloads from cmath, if that is the case and there is likely a implementation defined caveat here then we may be reverting to old C style behavior if the only version available is sqrt(double) which would mean that int would be converted to double implicitly.

One way I found to test this on gcc and clang would be to use long type for a which along with -Wconversion flag triggers a warning for a potentially value altering conversion on my platform if we only have sqrt(double) available. Indeed if I include math.h instead of cmath we can produce this warning. Although I can not trigger this behavior in clang which seems to indicate this is implementation dependent.

Roseleeroselia answered 30/10, 2013 at 14:24 Comment(1)
In the current standard, the relevant section is [cmath.syn]/2 "For each set of overloaded functions..."Subak
C
5

The 10 is being implicitly converted to a double. This will happen automatically as long as you have the correct function prototype for sqrt.

Edit: beaten by comments

Chiro answered 30/10, 2013 at 14:6 Comment(3)
There are three floating point overloads, which would be ambiguous and an error so there must be an explicit overload for integer cases.Roseleeroselia
@Shafik Yaghmour: You are right, but as you correctly noted in your answer the new spec intends to keep the legacy code valid, i.e. int arguments should "somehow" resolve to double overloads without triggering overload ambiguity error.Spermous
@nos That could be possible if the math.h is being used instead of cmath but if cmath if being used then I don't think that would be the case but that is probably implementation defined, let me amend my answer to cover both cases. Good point, thanks for pointing out the possibility.Roseleeroselia
R
5

Update 2

This question was merged with an exact duplicate, on taking a look at this, the actual answer is much simpler than anyone originally thought. The current version of std_lib_facilities.h includes the following line:

inline double sqrt(int x) { return sqrt(double(x)); }   // to match C++0x

which creates a specific overload for the int case to match what modern compilers should be be doing which is cast integer arguments to double, although this version does not cover all the cases.

If std_lib_facilities.h was not being used than the original logic still applies, although gcc-4.2 is rather old compared to Visual Studio 2012 from the original question but a 4.1.2 version have uses __builtin_sqrt specifically for the integer case.

Original

Since around 2005 the draft standard required integer argument to be cast to double this is covered in the draft C++ standard. If we look in section 26 Numerics library and then go to section 26.8 C library which covers the <cmath> header, it specifies overloads of the math functions for float, double and long double which is covered in paragraph 8:

In addition to the double versions of the math functions in , C++ adds float and long double overloaded versions of these functions, with the same semantics.

which would be ambiguous for the int case but the standard requires that sufficient overload are provided so that integer arguments are cast to double. It is covered in paragraph 11 which says(emphasis mine):

Moreover, there shall be additional overloads sufficient to ensure:

  1. If any arithmetic argument corresponding to a double parameter has type long double, then all arithmetic arguments corresponding to double parameters are effectively cast to long double.
  2. Otherwise, if any arithmetic argument corresponding to a double parameter has type double or an integer type, then all arithmetic arguments corresponding to double parameters are effectively cast to double.
  3. Otherwise, all arithmetic arguments corresponding to double parameters have type float.

Update

As @nos points out it is possible that the version of sqrt being called is from math.h header as opposed to the overloads from cmath, if that is the case and there is likely a implementation defined caveat here then we may be reverting to old C style behavior if the only version available is sqrt(double) which would mean that int would be converted to double implicitly.

One way I found to test this on gcc and clang would be to use long type for a which along with -Wconversion flag triggers a warning for a potentially value altering conversion on my platform if we only have sqrt(double) available. Indeed if I include math.h instead of cmath we can produce this warning. Although I can not trigger this behavior in clang which seems to indicate this is implementation dependent.

Roseleeroselia answered 30/10, 2013 at 14:24 Comment(1)
In the current standard, the relevant section is [cmath.syn]/2 "For each set of overloaded functions..."Subak
G
3

Because of implicit conversions. sqrt is defined for double, and an int value can be (and is) converted implicitly to a value of type double.

(It is in fact pretty hard to prevent a function that takes a double from being called with an int. You may get your compiler to emit a warning, but since this is typically a value-preserving conversion, even that may be hard. C++ inherits from C the design to try as hard as possible to make code work, even if it requires contortions. Other languages are much stricter about this sort of thing.)

Grammalogue answered 26/10, 2013 at 23:53 Comment(3)
About your second paragraph, wouldn't just putting in an overload of sqrt(int) = delete do it? I know a C++11 mention is coming, so how would that work differently from just not defining it in pre-C++11 apart from the nicer error?Suzette
@chris: I was speaking more generally. You can of course add individual overloads, but to cover all arithmetic types which are implicitly convertible to one another, you'd get into a mess. (Consider also having multiple arguments.)Grammalogue
Good point on the multiple parameters, though from testing a bit, the single parameter one seems to work pretty well with all built-in integral types.Suzette
C
2

sqrt is defined for double. And C++ allows you to convert int to double implicitly.

int n = 3;
double x = sqrt(n);    // implicit conversion of n from int to double

Implicit conversions may happen when you use the value as a function parameter or assign it to a variable.

An example for the second case would be:

int n = 3;
double x = n;          // implicit conversion of n from int to double

Note that operators are also simply functions. Thus, you can also add an int to a double, which converts the int to a double before invoking the actual addition:

int n = 3;
double x = 1.0;
double sum = n + x;    // implicit conversion of n from int to double
Clausius answered 26/10, 2013 at 23:54 Comment(0)
C
1

Because there's an implicit conversion from int to double.

With the conversion, your code would look like this:

cout << "Square root of n == " << sqrt((double)n) << "\n";
Countrywide answered 26/10, 2013 at 23:54 Comment(5)
Do you mean, cout << "Square root of n == " << sqrt(double(n)) << "\n"; ?Birthday
@sampablokuper, Both of those do the same thing.Suzette
@sampablokuper I wrote the conversion with C syntax, you wrote it in C++ syntax (I'm just used to the first form).Countrywide
@xorguy, I wouldn't really call it C++ form, seeing as how C++ also has static_cast et al (which are recommended over both of these). The proper name for it is a functional (function-style) cast.Suzette
@Suzette thanks for clarifying :) I'm having a look now at cplusplus.com/doc/tutorial/typecastingBirthday
V
1

Because the compiler is actually automatically (i.e. "implicitly") converting the integer to a double (or maybe long double) and sending that value to sqrt(). This is completely normally and completely legal.

Valdemar answered 26/10, 2013 at 23:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.