12

Is there a function in C that returns the maximum value of a variable like this (I will name the function "maxvalue" in example below)?

int a; printf("%d", maxvalue(a)); // 32767 unsigned int b; printf("%d", maxvalue(b)); // 65535 

So basically the function returns values like INT_MAX when the variable is signed INT, UINT_MAX when unsigned int, etc.

5
  • 1
    Might be possible with the new C11 with type-generic macros Commented Oct 6, 2012 at 17:29
  • 2
    C functions take as argument values, not types. For an unsigned integer type T, you can always compute the maximum value of that type with ((T)-1). For a signed integer type, I do not know any portable solution. Commented Oct 6, 2012 at 17:30
  • @Joachim - definitely possible. My answer has a complete example for the OP's case. My system has 32-bit int types, so the answers are larger, but it's the same idea. Commented Oct 6, 2012 at 17:37
  • @CarlNorum It's even possible without C11; see (the second half of) my answer. Commented Oct 6, 2012 at 21:06
  • @CarlNorum ... or not; see my afterthought. Commented Oct 6, 2012 at 21:19

7 Answers 7

11

Such a function is not defined by the C standard library. You can try defining a macro that calculates it:

#define MAX_VALUE(a) (((unsigned long long)1 << (sizeof(a) * CHAR_BIT)) - 1) 

When using it, be careful it is assigned to a type large enough. For example:

#include <stdlib.h> #include <stdio.h> #include <limits.h> #define IS_TYPE_SIGNED(a) ((a-1) < 0) #define MAX_VALUE_UNSIGNED(a) (((unsigned long long)1 << \ (sizeof(a) * CHAR_BIT)) - 1) #define MAX_VALUE_SIGNED(a) (MAX_VALUE_UNSIGNED(a) >> 1) #define MAX_VALUE(a) (IS_TYPE_SIGNED(a) ? \ MAX_VALUE_SIGNED(a) : MAX_VALUE_UNSIGNED(a)) int main(void) { unsigned int i = 0; signed int j = 0; printf("%llu\n", MAX_VALUE(i)); printf("%llu\n", MAX_VALUE(j)); return EXIT_SUCCESS; } 

This prints out:

4294967295 2147483647 
Sign up to request clarification or add additional context in comments.

11 Comments

And it needs a -1. Nice near-answer to the question anyway.
It does work on signed types - running that program above prints out 4294967296 on my machine, which sounds about right.
@Mike - no it doesn't work for signed types. I voted down your answer because it's wrong. 4294967295 is almost certainly the max for an unsigned type. MAX_VALUE for a signed integer should probably be giving back 2147483647. The maximum signed 32-bit number is 0x7fffffff, not, 0xffffffff, which your answer gives.
Sorry - read your parentheses wrong. Your program gives back 0x8000000, which is still wrong, but in a different way. It's neither INT_MAX, nor UINT_MAX. I guess it's -INT_MIN? There are weird macro errors, too - your (unsigned long long) cast just casts the 1 in your macro, not the whole result, which is the only reason it appears to work at all - you'd get a negative result if you had parentheses around your macro expansion.
(((unsigned long long)1 << (sizeof(a) * CHAR_BIT)) - 1) will not work for a's of type (unsigned) long long because of the overflow in the shift operator count. It's undefined behavior.
|
10

You can do it quite easily with a C11 type-generic expression:

#define maxvalue(type) _Generic(type, int: INT_MAX, \ unsigned int: UINT_MAX) 

It's not a function, but I think it does what you want. Here's a simple example program:

#include <stdio.h> #include <limits.h> #define maxvalue(type) _Generic(type, int: INT_MAX, \ unsigned int: UINT_MAX) int main(void) { int i; unsigned int ui; printf("%u\n", maxvalue(i)); printf("%u\n", maxvalue(ui)); return 0; } 

And its output:

$ clang -Wall -Werror -Wextra -pedantic -std=c11 example.c -o example $ ./example 2147483647 4294967295 

My answers are larger than yours because my system has 32-bit integers. You appear to have a 16-bit machine.

2 Comments

I didn't know you could get c to tell you the type of a variable. Neat.
@AndyHarris - pretty new feature. I'm not even sure which compilers have good C11 support. I mostly use clang/LLVM, which seems to handle it well.
5

Here are macros from my library that work for types, rather than variables:

/* min and max integer values. T is a signed or unsigned integer type. */ /* Returns 1 if T is signed, else 0. */ #define INTTYPE_SIGNED(T) ((T)-1 < (T)0) /* * Returns (T)(maximum value of a T). * * Pains are taken (perhaps unnecessarily) to avoid integer overflow * with signed types. */ #define INTTYPE_MAX(T) \ (((T)1 << (CHAR_BIT*sizeof(T)-INTTYPE_SIGNED(T)-1)) - 1 + \ ((T)1 << (CHAR_BIT*sizeof(T)-INTTYPE_SIGNED(T)-1))) /* * Returns (T)(minimum value of a T). * Pains are taken (perhaps unnecessarily) to avoid integer overflow * with signed types. * assert: twos complement architecture */ #define INTTYPE_MIN(T) ((T)(-INTTYPE_MAX(T)-1)) 

Edit: Adapting these to the question:

/* min and max integer values. V is a signed or unsigned integer value. */ /* Returns 1 if V has signed type, else 0. */ #define INT_VALUE_SIGNED(V) ((V)-(V)-1 < 0) /* * Returns maximum value for V's type. * * Pains are taken (perhaps unnecessarily) to avoid integer overflow * with signed types. */ #define INT_VALUE_MAX(V) \ (((V)-(V)+1 << (CHAR_BIT*sizeof(V)-INT_VALUE_SIGNED(V)-1)) - 1 + \ ((V)-(V)+1 << (CHAR_BIT*sizeof(V)-INT_VALUE_SIGNED(V)-1))) /* * Returns minimum value for V's type. * Pains are taken (perhaps unnecessarily) to avoid integer overflow * with signed types. * assert: twos complement architecture */ #define INT_VALUE_MIN(V) (-INT_VALUE_MAX(V)-1) 

Afterthought: These invoke UB if V is a variable, or an expression containing variables, that have not been assigned a value ... which is the case in the question as asked. They are likely to work on many implementations, but the C standard doesn't guarantee it, and they will certainly fail on an implementation that initializes uninitialized variables with trap values.

4 Comments

+1 for taking pains to avoid overflow and generality. -0.2 for tacitly assuming that no implementation is mad enough to have integer types with padding bits. Rounding up, though.
INT_VALUE_SIGNED() typically returns 1 for all of the following arguments: (signed char)1, (unsigned char)1, (short)1 and (unsigned short)1 because of [unsigned] char/short promotion to signed int. It will give "expected" results for unsigned char and unsigned short when they are the same size as int. Good answer otherwise, +1.
@AlexeyFrunze Good catch. Any ideas for fixing that?
I've found one way for variables (lvalues), but not for (r)values. I don't think you can fix that in C99- if you work with rvalues. See my answer.
2

You cannot create a function that does this, but you can create some macros that do this.

If you have C11 you can use _Generic:

#define maxvalue(x) \ _Generic(x, \ char: 127, short: 32767, int: INT_MAX, \ unsigned char: 255, unsigned short: 65535, unsigned int: UINT_MAX) 

If you need C89, you can do it if you can differentiate between signed/unsigned:

#define maxvalue_unsigned(x) ((1<<(8*sizeof(x)))-1) #define maxvalue_signed(x) ((1<<((8*sizeof(x)-1)))-1) 

If you're willing to require the typename (or use the GCC-specific typename) you could use strings:

#define maxvalue_type(x) maxvalue_helper(#x "----------") unsigned long long maxvalue_helper(const char *s) { switch(*s){ char 'c': /* char */ return 127; char 's': /* short */ return 32767; char 'i': /* int */ return INT_MAX; /* ... */ case 'u': /* unsigned */ switch(9[s]) { case 'c': /* unsigned char */ return 255; char 's': /* unsigned short */ return 65535; char 'i': /* unsigned int */ return UINT_MAX; /* ... */ 

1 Comment

C99 doesn't have _Generic. It's a C11 feature.
1

No, such a function does not exist in the standard C implementation.

Comments

1

Looks like I have been able to come up with a relatively easy-to-use macro, SIGNED_VAR(VAR), to test whether the given integer variable is signed by modifying, comparing and restoring the variable's value (all that is only necessary for types smaller than int) while avoiding undefined behavior, specifically the kinds related to signed overflow and sequence points. Or so it seems. At least, gcc (invoked with -Wall) doesn't complain about me doing crazy things between the && and || operators, although it didn't like the same kind of things around the ternary ?: operator.

The good thing about this macro is that it should work with C89 and C99 compilers (1LL can be replaced with 1L and long long can be replaced with just long (and "%ll" with "%l", of course) if your C89 compiler does not have the extended long long type from C99) and it also correctly supports types smaller than int (char and short).

Once we know whether the variable is signed or not, constructing the minimum and maximum values is trivial and many have show how to do it. The macros VAR_MAX() and VAR_MIN() construct these values and return them as the longest C99 integer type, long long. I chose to return the signed type to avoid potential overflow/UB problems when converting unsigned values to signed. Since the returned type long long cannot represent the maximum value of unsigned long long (ULLONG_MAX) directly as a signed value, if that value needs to be returned, a -1 is returned instead, which after a cast to unsigned long long will produce ULLONG_MAX. You need to be a little careful here.

Here goes the ugliness. Hope, I didn't miss a bug.

Oh, and, of course, it's expected that the entire asymmetric range of 2's complement values is supported in signed types (e.g. min=-128, max=+127).

EDIT: I've forgot to mention that SIGNED_VAR() expects the variable to be initialized. Otherwise, reading it can result in undefined behavior.

// file: IntVarMinMax.c // compile: gcc -Wall -std=c99 -O2 IntVarMinMax.c -o IntVarMinMax.exe #include <stdio.h> #include <stdlib.h> #include <limits.h> int SignTestTestVal; unsigned char SignTestOriginalXchar; unsigned short SignTestOriginalXshort; signed char SignTestRestoreOriginalXchar(void) { if (SignTestOriginalXchar < SCHAR_MAX + 1u) return (signed char)SignTestOriginalXchar; return (signed char)(SignTestOriginalXchar - SCHAR_MAX - 1) - SCHAR_MAX - 1; } short SignTestRestoreOriginalXshort(void) { if (SignTestOriginalXshort < SHRT_MAX + 1u) return (short)SignTestOriginalXshort; return (short)(SignTestOriginalXshort - SHRT_MAX - 1) - SHRT_MAX - 1; } #define IFELSE(E1,E2,E3) (((E1) && (E2)) || (!(E1) && (E3))) #define SEQ(E1,E2) (((E1) && (E2)) || (E2)) #define SIGNED_VAR(VAR) \ ( \ IFELSE \ ( \ sizeof(VAR) >= sizeof(int), \ ((VAR) - (VAR) - 1 < 0), \ IFELSE \ ( \ sizeof(VAR) == sizeof(short), \ SEQ(SignTestOriginalXshort = (VAR), \ SEQ(SignTestTestVal = (VAR) = -1, \ SEQ((VAR) = SignTestRestoreOriginalXshort(), \ SignTestTestVal < 0))), \ IFELSE \ ( \ sizeof(VAR) == sizeof(char), \ SEQ(SignTestOriginalXchar = (VAR), \ SEQ(SignTestTestVal = (VAR) = -1, \ SEQ((VAR) = SignTestRestoreOriginalXchar(), \ SignTestTestVal < 0))), \ (fprintf(stderr, "unsupported type!"), exit(-1), 0) \ ) \ ) \ ) \ ) #define VAR_MAX(SIGNED,VAR) \ ( \ SIGNED ? \ ((1ll << (sizeof(VAR) * CHAR_BIT - 2)) - 1 + \ (1ll << (sizeof(VAR) * CHAR_BIT - 2))) : \ ( \ (sizeof(VAR) < sizeof(long long)) ? \ ((1ll << (sizeof(VAR) * CHAR_BIT - 1)) - 1 + \ (1ll << (sizeof(VAR) * CHAR_BIT - 1))) : \ ( \ (sizeof(VAR) == sizeof(long long)) ? \ -1ll : \ (fprintf(stderr, "unsupported type!"), exit(-1), 0) \ ) \ ) \ ) #define VAR_MIN(SIGNED,VAR) \ ( \ SIGNED ? \ (-((1ll << (sizeof(VAR) * CHAR_BIT - 2)) - 1 + \ (1ll << (sizeof(VAR) * CHAR_BIT - 2))) - 1) : \ 0 \ ) int main(void) { signed char sc = 1; char c = 2; unsigned char uc = 3; short ss = 4; unsigned short us = 5; int si = 6; unsigned int ui = 7; long sl = 8; unsigned long ul = 9; long long sll = 10; unsigned long long ull = 11; #define PRINT_VARS() \ printf("sc=%hhd, c=%hhu, uc=%hhu, " \ "ss=%hd, us=%hu, si=%d, ui=%u, " \ "sl=%ld, ul=%lu, sll=%lld, ull=%llu\n", \ sc, c, uc, \ ss, us, si, ui, \ sl, ul, sll, ull) #define TEST_VAR(VAR) \ { \ int varIsSigned = SIGNED_VAR(VAR); \ if (varIsSigned) \ printf("%lld <= " #VAR " <= %lld\n", \ VAR_MIN(varIsSigned,VAR), \ VAR_MAX(varIsSigned,VAR)); \ else \ printf("%lld <= " #VAR " <= %llu\n", \ VAR_MIN(varIsSigned,VAR), \ (unsigned long long)VAR_MAX(varIsSigned,VAR)); \ } PRINT_VARS(); TEST_VAR(sc); TEST_VAR(c); TEST_VAR(uc); TEST_VAR(ss); TEST_VAR(us); TEST_VAR(si); TEST_VAR(ui); TEST_VAR(sl); TEST_VAR(ul); TEST_VAR(sll); TEST_VAR(ull); PRINT_VARS(); return 0; } 

Output (ideone):

sc=1, c=2, uc=3, ss=4, us=5, si=6, ui=7, sl=8, ul=9, sll=10, ull=11 -128 <= sc <= 127 -128 <= c <= 127 0 <= uc <= 255 -32768 <= ss <= 32767 0 <= us <= 65535 -2147483648 <= si <= 2147483647 0 <= ui <= 4294967295 -2147483648 <= sl <= 2147483647 0 <= ul <= 4294967295 -9223372036854775808 <= sll <= 9223372036854775807 0 <= ull <= 18446744073709551615 sc=1, c=2, uc=3, ss=4, us=5, si=6, ui=7, sl=8, ul=9, sll=10, ull=11 

1 Comment

I don’t even… WHAT’S THAT IFELSE DOING? There’s a ternary operator in C, you know…
1

Could easily be done using ANSI C 89:

 #include<stdio.h> #include<limits.h> int main(void) { printf("Max value of char: %d\n", CHAR_MAX); printf("Min value of char: %d\n", CHAR_MIN); printf("Max value of short: %d\n", SHRT_MAX); printf("Min value of short: %d\n", SHRT_MIN); printf("Max value of int: %d\n", INT_MAX); printf("Min value of int: %d\n", INT_MIN); printf("\n\n"); return 0; } 

Notice that you can include float.h and then use:

printf("Max value of Double: %d\n", DBL_MAX); 

Though, it is less recommended.

Good luck, Ron

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.