/********************************************
makebits.c
copyright 2024, Thomas E. Dickey

This is a source file for mawk, an implementation of
the AWK programming language.

Mawk is distributed without warranty under the terms of
the GNU General Public License, version 2, 1991.
********************************************/

/*
 * $MawkId: makebits.c,v 1.1 2024/11/17 20:33:16 tom Exp $
 */

#include "config.h"

#include <stdio.h>
#include <limits.h>
#include <sys/types.h>

#define my_name "_MAKEBITS_H"

static int
digits_of(double value)
{
    int result = 0;
    while (value >= 1.0) {
	value /= 10.0;
	++result;
    }
    return result;
}

static void
header(void)
{
    printf("/* this file was generated by makebits */\n");
    printf("\n");
    printf("#ifndef %s\n", my_name);
    printf("#define %s 1\n", my_name);
}

static void
section(const char *msg)
{
    printf("\n");
    printf("/* %s */\n", msg);
}

static void
footer(void)
{
    printf("\n");
    printf("#endif /* %s */\n", my_name);
}

int main(void)
{
    double unsigned_limits;
    double integers_limits;
    int unsigned_digits;
    int integers_digits;
    int n;
#ifdef HAVE_LONG_LONG
    union {
	double dd;
	unsigned long long ll;
    } samebits;
    unsigned len_significand;
    double max_significand;

    header();

    section("significand size");

    /*
     * Assume IEEE floating point, which means that "1.0" has all 0's to the
     * right of the exponent (which happens to be an odd number for "1.0").
     *
     * The maximum number of bits in the significand should be 53 for 32-bit
     * and 64-bit double format, provided that we have long long.
     */
    samebits.dd = 1.0;
    for (len_significand = 1; (samebits.ll & 1) == 0; ++len_significand) {
	samebits.ll /= 2;
    }
    printf("#define LEN_SIGNIFICAND %d\n", len_significand);

    /*
     * When converting to/from double, we are limited by the significand's
     * number of bits.
     */
    max_significand = 1.0;
    for (n = 0; n < (int) len_significand; ++n) {
	max_significand *= 2.0;
    }
    printf("#define MAX_SIGNIFICAND %.30g\n", max_significand);
#else
    header();
#endif

    section("limits for integer/floating conversion");

    /*
     * For maximum precision, use integer arithmetic (e.g., 2^32 - 1 for
     * unsigned values), plus the special case of the first power-of-two value
     * past the integer range.
     */
    unsigned_limits = 1.0;
    for (n = 0; n < (int) (CHAR_BIT * sizeof(double)); ++n) {
	unsigned_limits *= 2.0;
    }
    integers_limits = unsigned_limits / 2.0;

    printf("#define UNSIGNED_LIMITS %.30g.0\n", unsigned_limits);
    printf("#define INTEGERS_LIMITS %.30g.0\n", integers_limits);

    section("number of digits for integer/floating special case");
    unsigned_digits = digits_of(unsigned_limits);
    integers_digits = digits_of(integers_limits);
    printf("#define UNSIGNED_DIGITS %d\n", unsigned_digits);
    printf("#define INTEGERS_DIGITS %d\n", integers_digits);

    section("format for integer/floating special case");
    printf("#define UNSIGNED_FORMAT \"%%.%dg\"\n", unsigned_digits);
    printf("#define INTEGERS_FORMAT \"%%.%dg\"\n", integers_digits);

    footer();

    return 0;
}
