0

As far as I understood, NAN can be allocate to a double type variable but not necessarily to int / size_t

Is there still a way to populate a vector/matrix of type int / size_t with NAN?

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

typedef struct {
  size_t nrows, ncols;
  size_t *array;
} Matrix ;

size_t ncol = 5;
size_t nrow = 1;

void print_matrix(Matrix * matrix);

int main()
{
  Matrix *mat1 = (Matrix *) malloc(sizeof(Matrix));
  mat1->nrows = nrow;
  mat1->ncols = ncol;
  mat1->array = (size_t *) malloc(mat1->nrows * mat1->ncols * sizeof(*mat1->array));
  if (mat1 == NULL)
  {
    printf("Could not allocate memory\n");
    exit(EXIT_FAILURE);
  }
  else
  {
    mat1->nrows = nrow;
    mat1->ncols = ncol;
    mat1->array = (size_t *) malloc(mat1->nrows * mat1->ncols * sizeof(*mat1->array));
    for (size_t row =0; row<mat1->nrows; row++)
    {
      for (size_t col =0; col<mat1->ncols; col++)
      {
        mat1->array[row * ncol + col] = NAN;
      }
    }
    print_matrix(mat1);
    free(mat1);
  }
}

void print_matrix(Matrix * matrix)
{
  for (size_t row =0; row<matrix->nrows; row++)
  {
    for (size_t col =0; col<matrix->ncols; col++)
    {
      printf("%zu ", matrix->array[row * ncol + col]);
    }
    printf("\n");
  }
}
ecjb
  • 5,169
  • 12
  • 43
  • 79
  • 4
    `int` and `size_t` do not have a valid value which is NAN (or equivalent), so no. – wohlstad Mar 03 '23 at 12:58
  • 3
    `NaN` ("not a number") is a feature of floating-point number formats, and there's no standard equivalent for any integral types. Typically people want to use all the bits of an integer for storing values. You're welcome to define an integer constant which is invalid in your specific program, for this use, but it won't behave like `NaN` in arithmetic. – Useless Mar 03 '23 at 12:58
  • 3
    Integers just doesn't have the concept of "not a number" like floating point types have. Why do you need integer NAN's? What problem is that supposed to solve? – Some programmer dude Mar 03 '23 at 12:58
  • 2
    On a different note, please [don't cast the result of `malloc`](https://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc/605858) (or really any function returning `void *`). – Some programmer dude Mar 03 '23 at 12:59
  • 2
    This is like asking if I can populate a vector of type `int` with `3.1` – user253751 Mar 03 '23 at 12:59
  • 1
    You can determine a specific value which you'll intepret as NAN (e.g. 999999). This value must not be one of the actual valid values for this field in your application. – wohlstad Mar 03 '23 at 12:59
  • 1
    And `size_t` is to be used for *sizes*, not general integer values. If you need an unsigned integer type just use plain `unsigned` (or the `long` or `long long` variants). – Some programmer dude Mar 03 '23 at 13:00
  • 4
    If it's a sentinel value you're looking for, consider `INT_MIN`. And while this reduces the range of legally usable values, it makes positive and negative range equally sized. – Harith Mar 03 '23 at 13:02
  • thank you all for all comments and answers. @Someprogrammerdude : the problem that is supposed to solved is that I would like to populate a matrix at runtime, potentially incompletely, and then I want to iterate over the matrix and have a special flag where the cells remained untouched – ecjb Mar 03 '23 at 13:59
  • The C standard specifies trap representations, which can act as NaNs for integer types, but they are uncommon in modern C implementations. – Eric Postpischil Mar 03 '23 at 14:28
  • That kind of sounds like you want *sparse arrays* or similar.So you can keep track of which elements are occupied and which aren't. – Some programmer dude Mar 03 '23 at 16:22

1 Answers1

4

As far as I understood, NAN can be [assigned] to a double type variable

That is correct. NaN is one of the special values defined for IEEE-754-based types like float and double.1

but not necessarily to int / size_t

Right. NaN is not a value defined for integral types int or size_t.

You can't store NaN in an integral variable for the same reason you can't store 3.14 in an integral variable. It's just not one of the values that integral types can hold.

(One of the formal definitions of a data type in programming is that it is a set of values, and a set of operations on those values. For types float and double — but only for those types — NaN is one of the values. Actually, there are multiple NaN values, but that's a different story.)

For the floating-point types, NaNs are sentinel values, well-defined "not a value" values. Some types have sentinel values, and some don't. For example, for pointer types, NULL is the sentinel "not a valid pointer" value. In C strings, the null character \0 is a sentinel value marking the end of the string. On the other hand, when you call getchar, EOF is the "not a character" value. (This doesn't make EOF a sentinel value for type char, though, since EOF is not a value of type char.)

But there is no predefined sentinel value for any of the other integral types. The way integers are used in real programs, there's no value that could be reserved as a sentinel that might not also be a valid value.

If you have an integer variable and you need a sentinel value, you will need to define one of your own, if you can. For example, if you know that your values are always positive, you can use -1 as a sentinel value. But this will be, at best, a convention: the value -1 won't be treated specially by the language, or the CPU — it's just another integer value.

It's great that floating-point and pointer types have sentinel values, and it's sometimes a nuisance that other types don't. The ad hoc conventions one has to use when trying to define sentinel values for other types frequently lead to various nuisances. In order for EOF to work as a sentinel value for getchar, programmers have to remember to always store getchar's return value in a variable of type int, not char. A function like read that returns a number of characters read, or -1 to indicate an error, is difficult to define a good return type for, since it has to be signed to accommodate -1, but could otherwise be unsigned.2 The function mktime, which converts a broken-down time structure back to an integral time_t value, returns -1 for error, meaning you can't unambiguously tell whether you've gotten an error, or have successfully converted the time 23:59:59 on December 31, 1969.


Footnote 1: Types float and double aren't necessarily based on the IEEE-754 definitions in C, but on most systems these days, they are.

Footnote 2: I believe that's why read is defined as returning a value of type ssize_t, and I believe type ssize_t is a "signed size_t", or in other words, "A value that would otherwise be unsigned, just like size_t is, except it has to be signed so it can return -1".

Steve Summit
  • 45,437
  • 7
  • 70
  • 103
  • I do not think float or double are IEEE based. They existed long before IEEE 754. They might be coinciding with the IEEE types on compatible CPUs (e.g. x87) and compatible compilers. – Vladimir F Героям слава Mar 03 '23 at 13:22
  • @Vladimir Right. "Just about all machines today" (whatever that means) use IEEE-754, so it's a convenient assumption, but it's not universal. Footnote added. – Steve Summit Mar 03 '23 at 13:29
  • Re "I believe" in footnote 2, that is correct. Do note that `ssize_t` isn't a standard C type, though. It's a POSIX type. – ikegami Mar 03 '23 at 15:18
  • `ssize_t` isn't a standard C type, although the `printf` format specifier `%zd` is standard and requires the signed integer type that corresponds to `size_t`. (Similarly `%tu` is standard and requires the unsigned integer type that corresponds to `ptrdiff_t`.) I guess that allows an implementation not to implement a signed integer type corresponding to `size_t`. (But there *is* a requirement to implement an unsigned integer type corresponding to `ptrdiff_t`.) Every signed integer type is required to have a corresponding unsigned integer type, but not the other way round! – Ian Abbott Mar 03 '23 at 15:52