Rumor is that the next version of C will disallow sign magnitude and ones' complement signed integer encoding. True or not, it seems efficient to not have to code and test for those rare encodings.
[Edit 2025] C23 now only allows 2's complement encoding for signed integers.
Yet if code might not handle such cases as non-2's complement, it is prudent to detect and fail such compilations today.
Rather than just look for that one kind of dinosaur¹, below is C code that looks for various unicorns² and dinosaurs. Certainly some tests are more useful than others.
Review goal:
Please report any dinosaur¹ and unicorns² compilers found by this code.
Review how well this code would successfully flag true passé compilers and not report new innovative ones (e.g. 128-bit
intmax_t.)Suggest any additional or refined tests.
Pre-C11 compilers that lack
static_assertmay readily need a better#define static_assert ...than this code. Better alternatives are appreciated, but not a main goal of this post.
Note: I am not trying to rate strict adherence to IEEE_754 and the like.
Future readers concerning spelling and grammar in this post: Although they should get corrected in an answer, edits to the question's code are not site appropriate.
/*
* unicorn.h
* Various tests to detect old and strange compilers.
*
* Created on: Mar 8, 2019
* Author: chux
*/
#ifndef UNICORN_H_
#define UNICORN_H_
#include <assert.h>
#ifndef static_assert
#define static_assert( e, m ) typedef char _brevit_static_assert[!!(e)]
#endif
#include <float.h>
#include <limits.h>
#include <stdint.h>
/*
* Insure 2's complement
* Could also check various int_leastN_t, int_fastN_t
*/
static_assert(SCHAR_MIN < -SCHAR_MAX && SHRT_MIN < -SHRT_MAX &&
INT_MIN < -INT_MAX && LONG_MIN < -LONG_MAX &&
LLONG_MIN < -LLONG_MAX && INTMAX_MIN < -INTMAX_MAX &&
INTPTR_MIN < -INTPTR_MAX && PTRDIFF_MIN < -PTRDIFF_MAX
, "Dinosuar: Non-2's complement.");
/*
* Insure the range of unsigned is 2x that of positive signed
* Only ever seen one once with the widest unsigned and signed type with same max
*/
static_assert(SCHAR_MAX == UCHAR_MAX/2 && SHRT_MAX == USHRT_MAX/2 &&
INT_MAX == UINT_MAX/2 && LONG_MAX == ULONG_MAX/2 &&
LLONG_MAX == ULLONG_MAX/2 && INTMAX_MAX == UINTMAX_MAX/2,
"Dinosuar: narrowed unsigned.");
/*
* Insure char is sub-range of int
* When char values exceed int, makes for tough code using fgetc()
*/
static_assert(CHAR_MAX <= INT_MAX, "Dinosuar: wide char");
/*
* Insure char is a power-2-octet
* I suspect many folks would prefer just CHAR_BIT == 8
*/
static_assert((CHAR_BIT & (CHAR_BIT - 1)) == 0, "Dinosaur: Uncommon byte width.");
/*
* Only binary FP
*/
static_assert(FLT_RADIX == 2, "Dinosuar: Non binary FP");
/*
* Some light checking for pass-able FP types
* Certainly this is not a full IEEE check
* Tolerate float as double
*/
static_assert(sizeof(float)*CHAR_BIT == 32 || sizeof(float)*CHAR_BIT == 64,
"Dinosuar: Unusual float");
static_assert(sizeof(double)*CHAR_BIT == 64, "Dinosuar: Unusual double");
/*
* Heavier IEEE checking
*/
static_assert(DBL_MAX_10_EXP == 308 && DBL_MAX_EXP == 1024 &&
DBL_MIN_10_EXP == -307 && DBL_MIN_EXP == -1021 &&
DBL_DIG == 15 && DBL_DECIMAL_DIG == 17 && DBL_MANT_DIG == 53,
"Dinosuar: Unusual double");
/*
* Insure uxxx_t range <= int
* Strange when unsigned helper types promote to int
*/
static_assert(INT_MAX < UINTPTR_MAX, "Unicorn: narrow uintptr_t");
static_assert(INT_MAX < SIZE_MAX, "Unicorn: narrow size_tt");
/*
* Insure xxx_t range >= int
* Also expect signed helper types at least int range
*/
static_assert(INT_MAX <= PTRDIFF_MAX, "Unicorn: narrow ptrdiff_t");
static_assert(INT_MAX <= INTPTR_MAX, "Unicorn: narrow intptr_");
/*
* Insure all integers are within `float` finite range
*/
// Works OK when uintmax_t lacks padding
static_assert(FLT_RADIX == 2 && sizeof(uintmax_t)*CHAR_BIT < FLT_MAX_EXP,
"Unicorn: wide integer range");
// Better method
#define UNICODE_BW1(x) ((x) > 0x1u ? 2 : 1)
#define UNICODE_BW2(x) ((x) > 0x3u ? UNICODE_BW1((x)/0x4)+2 : UNICODE_BW1(x))
#define UNICODE_BW3(x) ((x) > 0xFu ? UNICODE_BW2((x)/0x10)+4 : UNICODE_BW2(x))
#define UNICODE_BW4(x) ((x) > 0xFFu ? UNICODE_BW3((x)/0x100)+8 : UNICODE_BW3(x))
#define UNICODE_BW5(x) ((x) > 0xFFFFu ? UNICODE_BW4((x)/0x10000)+16 : UNICODE_BW4(x))
#define UNICODE_BW6(x) ((x) > 0xFFFFFFFFu ? \
UNICODE_BW5((x)/0x100000000)+32 : UNICODE_BW5(x))
#define UNICODE_BW(x) ((x) > 0xFFFFFFFFFFFFFFFFu ? \
UNICODE_BW6((x)/0x100000000/0x100000000)+64 : UNICODE_BW6(x))
static_assert(FLT_RADIX == 2 && UNICODE_BW(UINTMAX_MAX) < FLT_MAX_EXP,
"Unicorn: wide integer range");
/*
* Insure size_t range > int
* Strange code when a `size_t` object promotes to an `int`.
*/
static_assert(INT_MAX < SIZE_MAX, "Unicorn: narrow size_t");
/*
* Recommended practice 7.19 4
*/
static_assert(PTRDIFF_MAX <= LONG_MAX, "Unicorn: ptrdiff_t wider than long");
static_assert(SIZE_MAX <= ULONG_MAX, "Unicorn: size_t wider thna unsigned long");
/*
* Insure range of integers within float
*/
static_assert(FLT_RADIX == 2 && sizeof(uintmax_t)*CHAR_BIT < FLT_MAX_EXP,
"Unicorn: wide integer range");
// Addition code could #undef the various UNICODE_BWn
#endif /* UNICORN_H_ */
Test driver
#include "unicorn.h"
#include <stdio.h>
int main(void) {
printf("Hello World!\n");
return 0;
}
¹ C is very flexible, yet some features applied to compilers simply no longer in use for over 10 years. For compilers that used out-of-favor features (non-2's complement, non-power-of-2 bit width "bytes", non-binary floating-point, etc.) I'll call dinosaurs.
² C is very flexible for new platform/compilers too. Some of these potential and theoretical compliers could employ very unusual features. I'll call these compilers unicorns. Should one appear, I rather have code fail to compile than compile with errant functioning code.
unsigned charis a sub-range ofintinstead? To wit,EOFbeing distinct is useful. \$\endgroup\$