Convert string to double and check for errors
Good use of errno, but can be improved.

The below is decent code for detecting overflow, yet would benefit with improvements.  

On overflow, the return value is HUGE_VAL, not necessarily DBL_MAX.  HUGE_VAL may be a large finite or an infinity.

if ((errno == ERANGE &amp;&amp; (val == DBL_MAX || val == -DBL_MAX)) || ...) {
    perror("strtod");
    exit(EXIT_FAILURE);
}
// replace with 
if ((errno == ERANGE &amp;&amp; (val == HUGE_VAL || val == -HUGE_VAL)) || ...) {


Underflow may also raise ERANGE.  Look at the code that attempts to discern that and review the spec.  

if (...  || (errno != 0 &amp;&amp; val == 0.0)) {
    perror("strtod");
    exit(EXIT_FAILURE);
}



  If the result underflows ..., the functions return a value whose magnitude is no greater than the smallest normalized positive number in the return type; whether errno acquires the value ERANGE is implementation-defined.


OP's code incorrectly assumes 0.0 on underflow.

With so  many options with 1) if errno is set and 2) what value is return, portable code nneds to fix a mess.  

Portable code would use the following, yet that gives up useful conversions by some of values in the sub-normal range DBL_TRUE_MIN to DBL_MIN.

if (fabs(val) &lt; DBL_MIN) {
  if (val &gt; 0.0) val = DBL_MIN;
  else if (val &lt; 0.0) val = -DBL_MIN;
  // val is now, DBL_MIN, -DBL_MIN, 0.0, or -0.0

  // pedantic code would also reset errno = 0 if it changed due to `strtod()`
}


A somewhat less portable handing, yet retains sub-normal values - just accept any wee value.

if (fabs(val) &lt; DBL_MIN) {
  // reset errno = 0 if it changed due to `strtod()`
}


No conversion

Good test for no conversion, yet some strtod() set errno in the no conversion case.  To simplify errno processing in the previous block, I'd recommend to do the endptr == str before looking at errno.

Use exponential notation for output

"%f" may print all values of magnitude less than 0.0000005 as 0.000000.  This is not informative.  With large values like DBL_MAX, perhaps hundreds of digits are printed of which past the first 20 or so are rarely of interest.

Instead:

#include &lt;float.h&gt;

printf("strtod() returned %.*g\n", DBL_DECIMAL_DIG, DBL_val);
// or
printf("strtod() returned %.*e\n", DBL_DECIMAL_DIG - 1, val);


Fancier code would assess the length of the argv[1] string for exponential notation and number of digits and then print out in a like-wise fashion.

Tolerate trailing white-space

OP code's alerts with input like "123  ", but not with "  123".  Suggest silence on trailing white-space.

// add
while (issapce((unsigned char) *endptr)) {
  endptr++;
}

if (*endptr != '\0')        /* Not necessarily an error... */
    printf("Further characters after number: %s\n", endptr);


Such non-numeric output may be best done on stderr rather than stdout.



Suggested alternative.  

Useful as a basis for some ideas.  

// Return status for my_strtod().
// Higher values are more problematic.
typedef enum {
  my_strtod_OK,
  my_strtod_Underflow,
  my_strtod_Overflow,
  my_strtod_ExtraJunk,
  my_strtod_NoConvertableText,
  my_strtod_N
} my_strtod_T;

// Convert a pointer to a string to double and saves its value in *dest.
// Return conversion status.
//
// `errno` is temporarily cleared by this function.  Its value on return:
//   1) Should strtod(*s, ...) set errno, then that is its value.
//   2) Otherwise errno is restored to its original value.
my_strtod_T my_strtod(double *dest, const char *s) {
  char *endptr;

  int errno_original = errno;
  errno = 0;
  *dest = strtod(s, &amp;endptr);
  int errno_my_strtod = errno;
  if (errno == 0) {
    errno = errno_original;
  }

  if (s == endptr) {
    return my_strtod_NoConvertableText;
  }

  while (isspace((unsigned char ) *endptr)) {
    endptr++;
  }
  if (*endptr) {
    return my_strtod_ExtraJunk;
  }

  if (errno_my_strtod == ERANGE &amp;&amp; fabs(*dest) == HUGE_VAL) {
    return my_strtod_Overflow;
  }

  if (errno_my_strtod == ERANGE &amp;&amp; fabs(*dest) &lt;= DBL_MIN) {
    return my_strtod_Underflow;
  }

  // Note: at this point, errno may be set
  return my_strtod_OK;
}


Selected C specs concerning errno: 

/*
 * errno ...  has type int ... the value of which is set to a positive error 
 * number by several library functions.  C11dr §7.5 2 
 * 
 * errno ... is never set to zero by any library function. The value of errno
 * may be set to nonzero by a library function call whether or not there is an 
 * error, provided the use of errno is not documented in the description of 
 * the function ... C11dr §7.5 3 
 * 
 * Of course, a library function can save the value of errno on entry and 
 * then set it to zero, as long as the original value is restored if errno’s 
 * value is still zero just before the return.  C11dr footnote 202
 */