Search code examples
cvisual-studiocompiler-errorssqrtmath.h

Check if a number is a perfect square in C


Task:

In an array of N elements (N < 20) of the unsigned long long type find the maximal full square and print its index (starting from zero). If there are no full squares, print -1. If there is more than one maximal square, print the index of the first one.

Input data: standard input stream, where N is written on the first line on the second line the array elements with a space.

Output data: a number in a standard output stream

Code:

#include <stdio.h>
#include <math.h>

int main() {
  
    unsigned short int n;
    unsigned long long mass[20];
    unsigned long long max = 0;
    int max_index = -1;
    
    scanf("%hu", &n);

    if (n >= 20) {

      do{
        
        scanf("%hu", &n);
        
      }while(n >= 20);
      
    }

    for (int i = 0; i < n; i++) {
        scanf("%llu", &mass[i]);

        long double root = (long double) sqrt(mass[i]);
        if (root * root == mass[i]) {

            if (mass[i] > max) {
                max = mass[i];
                max_index = i;
            }
        }
    }
    
    printf("%d\n", max_index);
    
    return 0;
}

I have this error while compiling

Error

I tried to use different types of data but it was useless.


Solution

  • At least these issues:

    Wrong compiler

    Error message indicates OP is using a C++ compiler when a C compiler should be used for C code. Function overloading (part of C++) is not a standard feature of C.

    Loss of precision

    sqrt(mass[i]) can loses precision. unsigned long long is at least a 64-bit type and converting that to double (to preform the square root) can lose needed precision. Floating point math introduces rounding issues too that can lead to incorrect results for this integer problem. Better to use integer math than floating-point math for an integer problem.

    Similar problems with root * root == mass[i]

    Consider forming a unsigned long long isqrt_ull(unsigned long long x) to find the square root.

    Sample:

    // Recursive version.
    unsigned long long isqrt_ull(unsigned long long x) {
      if (x < 2) {
        return x;
      }
      unsigned long long s = isqrt_ull(x >> 2) << 1;
      unsigned long long t = s + 1;
      return (t > x / t) ? s : t;
    }
    

    Corner case: 0

    If the array values are all 0, code reports -1 as mass[i] > max is never true. Better as mass[i] > max || max_index == -1


    Other minor issues

    • No need to test a new candidate for perfect square-ness unless it is greater than the prior max.

    • Avoid type proliferation: Use int for reading the array size in this learner's exercise.

    • Check return value of scanf() to know if input succeeded.

    • isqrt_ull() is not a highly efficient integer square root routine. It is just there as a simple placeholder that does not suffer floating point issues yet handles unsigned long long.