// SPDX-License-Identifier: CC0-1.0
// Author: Nominal Animal
#include <stdlib.h>
#include <inttypes.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <math.h>
/* This program assumes the following:
- 'float' type implements IEEE 754-2008 Binary32
- 'double' type implements IEEE 754-2008 Binary64
- integer and floating-point types have the same byte order
It does verify these assumptions in main(), too.
*/
typedef union {
uint64_t u64;
uint32_t u32[2];
uint16_t u16[4];
uint8_t u8[8];
int64_t i64;
int32_t i32[2];
int16_t i16[4];
int8_t i8[8];
float f[2];
double d;
} word64;
typedef union {
uint32_t u32;
uint16_t u16[2];
uint8_t u8[4];
int32_t i32;
int16_t i16[2];
int8_t i8[4];
float f;
} word32;
static inline word32 byteorder32(word32 value, int mask)
{
if (mask & 1)
value.u32 = ((value.u32 >> 8) & UINT32_C(0x00FF00FF)) | ((value.u32 & UINT32_C(0x00FF00FF)) << 8);
if (mask & 2)
value.u32 = ((value.u32 >> 16) & UINT32_C(0x0000FFFF)) | ((value.u32 & UINT32_C(0x0000FFFF)) << 16);
return value;
}
static inline word64 byteorder64(word64 value, int mask)
{
if (mask & 1)
value.u64 = ((value.u64 >> 8) & UINT64_C(0x00FF00FF00FF00FF))
| ((value.u64 & UINT64_C(0x00FF00FF00FF00FF)) << 8);
if (mask & 2)
value.u64 = ((value.u64 >> 16) & UINT64_C(0x0000FFFF0000FFFF))
| ((value.u64 & UINT64_C(0x0000FFFF0000FFFF)) << 16);
if (mask & 4)
value.u64 = ((value.u64 >> 32) & UINT64_C(0x00000000FFFFFFFF))
| ((value.u64 & UINT64_C(0x00000000FFFFFFFF)) << 32);
return value;
}
static inline const char *skip_whitespace(const char *src)
{
if (src)
while (*src == '\t' || *src == '\n' || *src == '\v' ||
*src == '\f' || *src == '\r' || *src == ' ')
src++;
return src;
}
static const char *float_bits(float value)
{
static char buf[64];
char *p = buf + sizeof buf;
uint32_t u = ((word32){ .f = value }).u32;
int n;
*(--p) = '\0';
n = 23; /* Binary32 has 23 bits in the mantissa. */
while (n-->0) {
*(--p) = '0' + (u & 1);
u >>= 1;
}
*(--p) = ')';
*(--p) = '0' + !!(u); /* Zero if high bits are zero, 1 otherwise */
*(--p) = '(';
*(--p) = ' ';
n = 8; /* Binary32 has 8 bits in the exponent. */
while (n-->0) {
*(--p) = '0' + (u & 1);
u >>= 1;
}
*(--p) = ' ';
/* Sign bit. */
*(--p) = '0' + (u & 1);
return p;
}
static const char *double_bits(double value)
{
static char buf[80];
char *p = buf + sizeof buf;
uint64_t u = ((word64){ .d = value }).u64;
int n;
*(--p) = '\0';
n = 52; /* Binary64 has 52 bits in the mantissa. */
while (n-->0) {
*(--p) = '0' + (u & 1);
u >>= 1;
}
*(--p) = ')';
*(--p) = '0' + !!(u); /* Zero if high bits are zero, 1 otherwise */
*(--p) = '(';
*(--p) = ' ';
n = 11; /* Binary64 has 11 bits in the exponent. */
while (n-->0) {
*(--p) = '0' + (u & 1);
u >>= 1;
}
*(--p) = ' ';
/* Sign bit. */
*(--p) = '0' + (u & 1);
return p;
}
static const char *u32_bits(uint32_t u)
{
static char buf[64];
char *p = buf + sizeof buf;
int n;
*(--p) = '\0';
n = 32;
while (n-->0) {
*(--p) = '0' + (u & 1);
u >>= 1;
if (n && !(n & 7))
*(--p) = '\'';
}
return p;
}
static const char *u64_bits(uint64_t u)
{
static char buf[80];
char *p = buf + sizeof buf;
int n;
*(--p) = '\0';
n = 64;
while (n-->0) {
*(--p) = '0' + (u & 1);
u >>= 1;
if (n && !(n & 7))
*(--p) = '\'';
}
return p;
}
int parse_intmax(const char *src, intmax_t *to)
{
const char *end;
intmax_t val;
end = src;
errno = 0;
val = strtoimax(src, (char **)&end, 0);
if (errno)
return -1;
if (end == src || *end)
return -1;
if (to)
*to = val;
return 0;
}
int parse_uintmax(const char *src, uintmax_t *to)
{
const char *end;
uintmax_t val;
end = src;
errno = 0;
val = strtoumax(src, (char **)&end, 0);
if (errno)
return -1;
if (end == src || *end)
return -1;
if (to)
*to = val;
return 0;
}
static const char *next_float(const char *src, float *to)
{
const char *end;
float val;
end = src;
errno = 0;
val = strtof(src, (char **)&end);
if (errno)
return NULL;
if (end == src)
return NULL;
if (!isfinite(val))
return NULL;
if (to)
*to = val;
return end;
}
static const char *next_double(const char *src, double *to)
{
const char *end;
double val;
end = src;
errno = 0;
val = strtod(src, (char **)&end);
if (errno)
return NULL;
if (end == src)
return NULL;
if (!isfinite(val))
return NULL;
if (to)
*to = val;
return end;
}
int parse_double(const char *src, double *to)
{
const char *end;
double val;
if (!src || !*src)
return -1;
end = skip_whitespace(next_double(src, &val));
if (!end || *end)
return -1;
if (!isfinite(val))
return -1;
if (to)
*to = val;
return 0;
}
int parse_double_fraction(const char *src, double *to)
{
const char *end;
double val, n, d;
if (!src || !*src)
return -1;
end = skip_whitespace(next_double(src, &n));
if (!end)
return -1;
if (*end == '/' || *end == ':')
end++;
else
return -1;
end = skip_whitespace(next_double(end, &d));
if (!end || *end)
return -1;
if (!d)
return -1;
val = n / d;
if (!isfinite(val))
return -1;
if (to)
*to = val;
return 0;
}
int parse_float(const char *src, float *to)
{
const char *end;
float val;
if (!src || !*src)
return -1;
end = skip_whitespace(next_float(src, &val));
if (!end || (*end != 'f' && *end != 'F'))
return -1;
if (!isfinite(val))
return -1;
if (to)
*to = val;
return 0;
}
int parse_float_fraction(const char *src, float *to)
{
const char *end;
float val, n, d;
int f = 0;
if (!src || !*src)
return -1;
end = skip_whitespace(next_float(src, &n));
if (!end)
return -1;
if (*end == 'f' || *end == 'F') {
end = skip_whitespace(end + 1);
f = 1;
}
if (*end == '/' || *end == ':')
end++;
else
return -1;
end = skip_whitespace(next_float(end, &d));
if (*end == 'f' || *end == 'F') {
end = skip_whitespace(end + 1);
f = 1;
}
if (!end || *end || !f)
return -1;
if (!d)
return -1;
val = n / d;
if (!isfinite(val))
return -1;
if (to)
*to = val;
return 0;
}
static const char *float_string(float value)
{
static char buf[1024];
int len;
len = snprintf(buf, sizeof buf, "%.32f", value);
if (len < 1 || len >= (int)sizeof buf)
return "(BUG!)";
while (len > 1 && buf[len-1] != '.' && buf[len-2] != '.') {
char old = buf[--len];
float tmp;
buf[len] = '\0';
if (!next_float(buf, &tmp) || tmp != value) {
buf[len] = old;
break;
}
}
return buf;
}
static const char *double_string(double value)
{
static char buf[1024];
int len;
len = snprintf(buf, sizeof buf, "%.64f", value);
if (len < 1 || len >= (int)sizeof buf)
return "(BUG!)";
while (len > 1 && buf[len - 1] != '.' && buf[len - 2] != '.') {
char old = buf[--len];
double tmp;
buf[len] = '\0';
if (!next_double(buf, &tmp) || tmp != value) {
buf[len] = old;
break;
}
}
return buf;
}
int find_float_fraction(float value, int32_t *nto, uint32_t *dto)
{
for (uint32_t d = 1; d < 33554432; d++) {
int32_t n = (int32_t)((double)value * (double)d - 0.5);
if ((float)((float)n / (float)d) == value) {
if (nto)
*nto = n;
if (dto)
*dto = d;
return 0;
}
n++;
if ((float)((float)n / (float)d) == value) {
if (nto)
*nto = n;
if (dto)
*dto = d;
return 0;
}
n++;
if ((float)((float)n / (float)d) == value) {
if (nto)
*nto = n;
if (dto)
*dto = d;
return 0;
}
}
return -1;
}
int find_double_fraction(double value, int32_t *nto, uint32_t *dto)
{
for (uint32_t d = 1; d < 33554432; d++) {
int32_t n = (int32_t)((double)value * (double)d - 0.5);
if ((double)((double)n / (double)d) == value) {
if (nto)
*nto = n;
if (dto)
*dto = d;
return 0;
}
n++;
if ((double)((double)n / (double)d) == value) {
if (nto)
*nto = n;
if (dto)
*dto = d;
return 0;
}
n++;
if ((double)((double)n / (double)d) == value) {
if (nto)
*nto = n;
if (dto)
*dto = d;
return 0;
}
}
return -1;
}
void show32(const char *src, word32 w)
{
int32_t n;
uint32_t d;
printf("%s\n", src);
printf(" Hex: 0x%" PRIx32 "\n", w.u32);
if (w.i32 < 0)
printf(" Dec: %" PRIu32 " = %" PRIi32 "\n", w.u32, w.i32);
else
printf(" Dec: %" PRIu32 "\n", w.u32);
printf(" Bin: 0b%s\n", u32_bits(w.u32));
printf(" Float: %s = %s\n", float_bits(w.f), float_string(w.f));
fflush(stdout);
if (!find_float_fraction(w.f, &n, &d))
printf(" Frac: %" PRIi32 " / %" PRIu32 "\n", n, d);
printf("\n");
fflush(stdout);
}
void show64(const char *src, word64 w)
{
int32_t n;
uint32_t d;
printf("%s\n", src);
printf(" Hex: 0x%" PRIx64 "\n", w.u64);
if (w.i64 < 0)
printf(" Dec: %" PRIu64 " = %" PRIi64 "\n", w.u64, w.i64);
else
printf(" Dec: %" PRIu64 "\n", w.u64);
printf(" Bin: 0b%s\n", u64_bits(w.u64));
printf(" Double: %s = %s\n", double_bits(w.d), double_string(w.d));
fflush(stdout);
if (!find_double_fraction(w.d, &n, &d))
printf(" Frac: %" PRIi32 " / %" PRIu32 "\n", n, d);
printf("\n");
fflush(stdout);
}
int main(int argc, char *argv[])
{
if (((word32){ .f = 992.0f / 19883.0f }).u32 != UINT32_C(0x3D4C5B6A))
fprintf(stderr, "Warning: Invalid 32-bit byte order, or 'float' not IEEE 754-2008 Binary32.\n");
if (((word64){ .d = -138242.0 / 65025.0 }).u64 != UINT64_C(0xc001020304050607))
fprintf(stderr, "Warning: Invalid 64-bit byte order, or 'double' not IEEE 754-2008 Binary64.\n");
if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
const char *arg0 = (argc > 0 && argv && argv[0] && argv[0][0]) ? argv[0] : "(this)";
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", arg0);
fprintf(stderr, " %s VALUE [ VALUE ... ]\n", arg0);
fprintf(stderr, "Where VALUE can be\n");
fprintf(stderr, " - a 32-bit integer (in octal, decimal, or hexadecimal)\n");
fprintf(stderr, " - a 32-bit floating point number (with 'f' suffix)\n");
fprintf(stderr, " - a pair of 32-bit floating point numbers with / in between,\n");
fprintf(stderr, " to denote a fraction (at least one with 'f' suffix)\n");
fprintf(stderr, " - a 64-bit integer (in octal, decimal, or hexadecimal)\n");
fprintf(stderr, " - a 64-bit floating point number (without any suffix)\n");
fprintf(stderr, " - a pair of 64-bit floating point numbers with / in between,\n");
fprintf(stderr, " to denote a fraction (neither having any suffix)\n");
fprintf(stderr, "\n");
return EXIT_SUCCESS;
}
for (int arg = 1; arg < argc; arg++) {
uintmax_t u;
intmax_t i;
double d;
float f;
if (!parse_uintmax(argv[arg], &u)) {
if ((uintmax_t)((uint32_t)u) == u) {
show32(argv[arg], (word32){ .u32 = u });
continue;
} else
if ((uintmax_t)((uint64_t)u) == u) {
show64(argv[arg], (word64){ .u64 = u });
continue;
}
}
if (!parse_intmax(argv[arg], &i)) {
if ((intmax_t)((int32_t)i) == i) {
show32(argv[arg], (word32){ .i32 = i });
continue;
} else
if ((intmax_t)((int64_t)i) == i) {
show64(argv[arg], (word64){ .i64 = i });
continue;
}
}
if (!parse_float(argv[arg], &f) || !parse_float_fraction(argv[arg], &f)) {
show32(argv[arg], (word32){ .f = f });
continue;
}
if (!parse_double(argv[arg], &d) || !parse_double_fraction(argv[arg], &d)) {
show64(argv[arg], (word64){ .d = d });
continue;
}
fprintf(stderr, "%s: Cannot parse parameter.\n", argv[arg]);
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}