Why Does Sqrt() Work Fine on an Int Variable If It Is Not Defined for an Int

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

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.

sqrt() - Why am I allowed to provide an argument of int and not only double and the output is also right?

This is not undefined behavior.

The function is defined to accept an argument of type double. Because the type of the argument is known, you can pass an int because it may be implicitly converted to a double. It's the same as if you did:

int i = 4;
double d = i;

The rules for conversion of function arguments are spelled out in section 6.5.2.2p7 of the C standard regarding the function call operator ():

If the expression that denotes the called function has a type that
does include a prototype, the arguments are implicitly converted, as
if by assignment, to the types of the corresponding parameters, taking
the type of each parameter to be the unqualified version of its
declared type.
The ellipsis notation in a function prototype
declarator causes argument type conversion to stop after the last
declared parameter. The default argument promotions are performed on
trailing arguments

In contrast, if you passed an int to printf when the format string expects a double, i.e.:

printf("%f\n", 4);

Then you have undefined behavior. This is because the types of the arguments are not known at compile time so the implicit conversion can't happen.

Why does sqrt function not work for values taken through user input?

Like was mentioned in the comments, you haven't linked against libm so sqrt is undefined at link-time.

Why would a constant value work for sqrt, while a variable user input value won't?

Because GCC recognizes sqrt as a builtin function and is able to evaluate the square root of a compile-time constant at compilation time, and forgoes the call to sqrt altogether, thus avoiding the subsequent linker error.

The ISO C90 functions ... sqrt, ... are all recognized as built-in functions unless -fno-builtin is specified (or -fno-builtin-function is specified for an individual function).

If you were to add -fno-builtin-sqrt, you would see the linker error regardless of what you pass to sqrt.

C: datatypes. sqrt function working with int why?

Why are all these [two initializations] work?

The first initialization float b = sqrt(1234) works because the language "upcasts" the integer literal 1234 to double before calling sqrt, and then converting the result to float.

The second initialization int b = sqrt(1234.22) works for the same reason, except this time the compiler does not have to upcast 1234.22 literal before the call, because it is already of type double.

This is discussed in C99 standard:

6.3.1.4.1: When a finite value of real floating type is converted to an integer type other than _Bool, the fractional part is discarded.

6.7.8.11: The initializer for a scalar shall be a single expression, optionally enclosed in braces. The initial value of the object is that of the expression (after conversion) (emphasis added).

-

why do we need this [cast int a = (int) b;]?

You may insert a cast for readability, but the standard does not require it (demo).

sqrt() of int type in C

Type conversions which do not cause a loss in precision might not throw warnings. They are cast implicitly.

int --> double //no loss in precision (e.g 3 became 3.00)
double --> int //loss in precision (e.g. 3.01222 became 3)

What triggers a warning and what doesn't is depends largely upon the compiler and the flags supplied to it, however, most compilers (atleast the ones I've used) don't consider implicit type-conversions dangerous enough to warrant a warning, as it is a feature in the language specification.


To warn or not to warn:

C99 Rationale states it like a guideline

One of the important outcomes of exploring this (implicit casting) problem is the understanding that high-quality compilers might do well to look
for such questionable code and offer (optional) diagnostics, and that
conscientious instructors might do well to warn programmers of the
problems of implicit type conversions.

C99 Rationale (Apr 2003) : Page 45

sqrt() function not working with variable arguments

You need to link with the math library (use a '-lm' on the command line). In the constant case, the compiler is probably being smart and precomputing sqrt(2.0) (so the code that is compiled is essentially 'b = 1.414...;')

Using (int) sqrt(N) causes 'array bound is not an integer constant'

int arr[size];

This defines an array with a constant size. But size was computed by a call to sqrt, so it's not a constant according to how C programs are executed.

The compiler needs to know how big the array is to create the global memory layout of the program. So the sqrt can't be deferred until runtime. And C doesn't have any concept of a "math" function that can be resolved at compile time.

The only way through this is to perform the calculation yourself and put the result (2) directly into the source code.

Why am I getting undefined reference to sqrt error even though I include math.h header?

The math library must be linked in when building the executable. How to do this varies by environment, but in Linux/Unix, just add -lm to the command:

gcc test.c -o test -lm

The math library is named libm.so, and the -l command option assumes a lib prefix and .a or .so suffix.



Related Topics



Leave a reply



Submit