192 lines
6.6 KiB
C
192 lines
6.6 KiB
C
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||
|
|
||
|
/**
|
||
|
* \file numlib.h
|
||
|
*
|
||
|
* Fast locale independent conversion from numerics (integer or floating point
|
||
|
* types) to ASCII and back.
|
||
|
*
|
||
|
* \copyright The DoubleFourteen Code Forge (C) All Rights Reserved
|
||
|
* \author Lorenzo Cogotti
|
||
|
*/
|
||
|
|
||
|
#ifndef DF_NUMLIB_H_
|
||
|
#define DF_NUMLIB_H_
|
||
|
|
||
|
#include "xpt.h"
|
||
|
|
||
|
/**
|
||
|
* \brief Integral compile-time costant representing an upper-bound estimate
|
||
|
* of the number of digit-characters necessary to hold the **decimal**
|
||
|
* representation of `typ`.
|
||
|
*
|
||
|
* Can be used as such:
|
||
|
* ```c
|
||
|
* char buf[1 + DIGS(int) + 1]; // sign + digits + '\0'
|
||
|
*
|
||
|
* Itoa(INT_MAX, buf);
|
||
|
* ```
|
||
|
*
|
||
|
* Approximation is derived from the following formula, where `MAX_INT` is
|
||
|
* the maximum integral value representable by `typ`:
|
||
|
* ```
|
||
|
* N = ceil(log10(MAX_INT)) ->
|
||
|
* N = floor(log10(MAX_INT)) + 1 ->
|
||
|
* (as base-256 logarithm)
|
||
|
* N = floor( log256(MAX_INT) / log256(10) ) + 1 ->
|
||
|
*
|
||
|
* N <= floor( sizeof(typ) * 2.40824 ) + 1 ->
|
||
|
*
|
||
|
* N ~= 241 * sizeof(typ) / 100 + 1
|
||
|
* ```
|
||
|
*
|
||
|
* \param typ An **integer** type or expression
|
||
|
*
|
||
|
* \author [John Bollinger](https://stackoverflow.com/a/43789115)
|
||
|
*/
|
||
|
#define DIGS(typ) ((241 * sizeof(typ)) / 100 + 1)
|
||
|
// #define DIGS(typ) (sizeof(typ) * CHAR_BIT) gross upper-bound approx
|
||
|
|
||
|
/**
|
||
|
* \brief Integral compile-time constant representing an upper-bound estimate
|
||
|
* of the number of digit-characters necessary to hold the **hexadecimal**
|
||
|
* representation of `typ`.
|
||
|
*
|
||
|
* \param typ An **integer** type or expression
|
||
|
*/
|
||
|
#define XDIGS(typ) (sizeof(typ) * 2)
|
||
|
|
||
|
/**
|
||
|
* \brief Maximum number of characters returned by `Ftoa()`,
|
||
|
* **excluding terminating `\0` char**.
|
||
|
*
|
||
|
* Range of double (IEEE-754 `binary64`): `[1.7E-308 ... 1.7E308]`
|
||
|
* - 1 char for sign
|
||
|
* - 309 digits for integer part
|
||
|
* - 1 char for mantissa dot
|
||
|
* - 37 chars for mantissa
|
||
|
*/
|
||
|
#define DOUBLE_STRLEN (309 + 39)
|
||
|
|
||
|
STATIC_ASSERT(sizeof(double) <= 8, "DOUBLE_STRLEN might be inaccurate on this platform");
|
||
|
// should actually make sure we also have IEEE-754 floats...
|
||
|
|
||
|
/**
|
||
|
* \brief Unsigned integer to ASCII conversion.
|
||
|
*
|
||
|
* Destination buffer is assumed to be large enough to hold the
|
||
|
* result. A storage of `DIGS(x) + 1` chars is guaranteed
|
||
|
* to be sufficient. Resulting string is always `NUL` terminated.
|
||
|
*
|
||
|
* \param [in] x Value to be converted
|
||
|
* \param [out] dest Destination string, must not be `\0`
|
||
|
*
|
||
|
* \return Pointer to the trailing `\0` char inside `dest`, useful
|
||
|
* for further string concatenation.
|
||
|
*/
|
||
|
char *Utoa(unsigned long long x, char *dest);
|
||
|
/**
|
||
|
* \brief Signed integer to ASCII conversion.
|
||
|
*
|
||
|
* Destination buffer is assumed to be large enough to hold the result.
|
||
|
* A storage of `1 + DIGS(x) + 1` chars, accounting for the sign
|
||
|
* character, is guaranteed to be sufficient.
|
||
|
*
|
||
|
* \param [in] x Value to be converted
|
||
|
* \param [out] dest Destination string, must not be `NULL`
|
||
|
*
|
||
|
* \return Pointer to the trailing `\0` char in `dest`, useful for further
|
||
|
* string concatenation.
|
||
|
*/
|
||
|
char *Itoa(long long x, char *dest);
|
||
|
/**
|
||
|
* \brief Unsigned integer to hexadecimal lowercase ASCII string.
|
||
|
*
|
||
|
* Destination buffer is assumed to be large enough to hold the result.
|
||
|
* A storage of `XDIGS(x) + 1` chars is guaranteed to be
|
||
|
* sufficient. Resulting string is always `\0` terminated.
|
||
|
*
|
||
|
* \param [in] x Value to be converted
|
||
|
* \param [out] dest Destination string, must not be `NULL`
|
||
|
*
|
||
|
* \return Pointer to the trailing `\0` char in `dest`, useful for further
|
||
|
* string concatenation.
|
||
|
*
|
||
|
* \note No `0x` prefix is prepended to resulting string.
|
||
|
*/
|
||
|
char *Xtoa(unsigned long long x, char *dest);
|
||
|
|
||
|
/**
|
||
|
* \brief Floating point number to scientific notation string.
|
||
|
*
|
||
|
* Destination string is assumed to be large enough to store the conversion
|
||
|
* result, a buffer of size `DOUBLE_STRLEN + 1` is guaranteed to be large
|
||
|
* enough for it. Result is always `\0` terminated.
|
||
|
*
|
||
|
* \param [in] x Floating point number to be converted
|
||
|
* \param [out] dest Destination for result string, must not be `NULL`
|
||
|
*
|
||
|
* \return Pointer to the trailing `\0` char inside result, useful for
|
||
|
* further string concatenation.
|
||
|
*/
|
||
|
char *Ftoa(double x, char *dest);
|
||
|
|
||
|
/// Numeric conversion outcomes.
|
||
|
typedef enum {
|
||
|
NCVENOERR = 0, ///< Conversion successful
|
||
|
NCVEBADBASE, ///< The specified numeric base is out of range
|
||
|
NCVENOTHING, ///< No legal numeric data in input string
|
||
|
NCVEOVERFLOW, ///< Numeric input too large for target integer type
|
||
|
NCVEUNDERFLOW ///< Numeric input too small for target integer type
|
||
|
} NumConvRet;
|
||
|
|
||
|
/**
|
||
|
* \brief ASCII to integer conversion.
|
||
|
*
|
||
|
* If `base` is 0 then the actual numeric base is guessed from input string
|
||
|
* prefix according to an extended C convention:
|
||
|
*
|
||
|
* Numeric prefix | Base
|
||
|
* ---------------|---------------------
|
||
|
* __0__ | octal, base 8
|
||
|
* __0x__ | hexadecimal, base 16
|
||
|
* __0b__ | binary, base 2
|
||
|
* __otherwise__ | decimal, base 10
|
||
|
*
|
||
|
* \param [in] s Input string to be converted, must not be `NULL`
|
||
|
* \param [out] endp Storage where end pointer is returned, may be `NULL`
|
||
|
* \param [in] base Integer conversion base, a value between 0 and 36 inclusive
|
||
|
* \param [out] outcome Storage where conversion outcome is returned, may be `NULL`
|
||
|
*
|
||
|
* \return Conversion result, 0 on error (use `outcome` to detect error).
|
||
|
*
|
||
|
* @{
|
||
|
* \fn unsigned long long Atoull(const char *, char **, unsigned, NumConvRet *)
|
||
|
* \fn unsigned long Atoul(const char *, char **, unsigned, NumConvRet *)
|
||
|
* \fn unsigned Atou(const char *, char **, unsigned, NumConvRet *)
|
||
|
* \fn long long Atoll(const char *, char **, unsigned, NumConvRet *)
|
||
|
* \fn long Atol(const char *, char **, unsigned, NumConvRet *)
|
||
|
* \fn int Atoi(const char *, char **, unsigned, NumConvRet *)
|
||
|
* @}
|
||
|
*/
|
||
|
unsigned long long Atoull(const char *s, char **endp, unsigned base, NumConvRet *outcome);
|
||
|
unsigned long Atoul(const char *s, char **endp, unsigned base, NumConvRet *outcome);
|
||
|
unsigned Atou(const char *s, char **endp, unsigned base, NumConvRet *outcome);
|
||
|
|
||
|
long long Atoll(const char *s, char **endp, unsigned base, NumConvRet *outcome);
|
||
|
long Atol(const char *s, char **endp, unsigned base, NumConvRet *outcome);
|
||
|
int Atoi(const char *s, char **endp, unsigned base, NumConvRet *outcome);
|
||
|
|
||
|
/**
|
||
|
* \brief ASCII to floating point conversion.
|
||
|
*
|
||
|
* \param [in] s Input string to be converted, must not be `NULL`
|
||
|
* \param [out] endp Storage where end pointer is returned, must not be `NULL`
|
||
|
* \param [out] outcome Storage where conversion outcome is returned, may be `NULL`
|
||
|
*
|
||
|
* \return Conversion result, 0 on error (use `outcome` to detect error).
|
||
|
*/
|
||
|
double Atof(const char *s, char **endp, NumConvRet *outcome);
|
||
|
|
||
|
#endif
|