mirror of
https://gitee.com/Lamdonn/varch.git
synced 2025-12-06 16:56:42 +08:00
4068 lines
155 KiB
C
4068 lines
155 KiB
C
/*********************************************************************************************************
|
|
* ------------------------------------------------------------------------------------------------------
|
|
* file description
|
|
* ------------------------------------------------------------------------------------------------------
|
|
* \file floatl.c
|
|
* \unit floatl
|
|
* \brief This is a simple large float number calculate module for C language
|
|
* \author Lamdonn
|
|
* \version v1.1.1
|
|
* \license GPL-2.0
|
|
* \copyright Copyright (C) 2023 Lamdonn.
|
|
********************************************************************************************************/
|
|
#include "floatl.h"
|
|
|
|
/**
|
|
* \brief A union for representing a single - precision floating - point number in different forms.
|
|
*
|
|
* This union allows accessing a single - precision floating - point number (float) in three different ways:
|
|
* 1. As a standard 'float' type.
|
|
* 2. As an unsigned 32 - bit integer.
|
|
* 3. As a structured representation with separate fields for the sign, exponent, and mantissa.
|
|
*/
|
|
typedef union {
|
|
// Access the value as a standard single - precision floating - point number
|
|
float float_;
|
|
// Access the binary representation of the floating - point number as an unsigned 32 - bit integer
|
|
uint32_t int_;
|
|
// Access the individual components of the floating - point number (sign, exponent, mantissa)
|
|
struct
|
|
{
|
|
// The mantissa of the floating - point number, occupying 23 bits
|
|
uint32_t mantissa : 23;
|
|
// The exponent of the floating - point number, occupying 8 bits
|
|
uint32_t exponent : 8;
|
|
// The sign of the floating - point number, occupying 1 bit (0 for positive, 1 for negative)
|
|
uint32_t sign : 1;
|
|
};
|
|
} float_u;
|
|
|
|
/**
|
|
* \brief A union for representing a double - precision floating - point number in different forms.
|
|
*
|
|
* This union allows accessing a double - precision floating - point number (double) in three different ways:
|
|
* 1. As a standard 'double' type.
|
|
* 2. As a structure containing two 32 - bit unsigned integers (low and high parts).
|
|
* 3. As a structured representation with separate fields for the sign, exponent, and mantissa.
|
|
*/
|
|
typedef union {
|
|
// Access the value as a standard double - precision floating - point number
|
|
double float_;
|
|
// Access the binary representation of the floating - point number as two 32 - bit unsigned integers
|
|
struct
|
|
{
|
|
// The lower 32 bits of the binary representation
|
|
uint32_t low;
|
|
// The upper 32 bits of the binary representation
|
|
uint32_t high;
|
|
} int_;
|
|
// Access the individual components of the floating - point number (sign, exponent, mantissa)
|
|
struct
|
|
{
|
|
// The lower part of the mantissa of the floating - point number
|
|
uint32_t mantissa_l;
|
|
// The upper 20 bits of the mantissa of the floating - point number
|
|
uint32_t mantissa_h : 20;
|
|
// The exponent of the floating - point number, occupying 11 bits
|
|
uint32_t exponent : 11;
|
|
// The sign of the floating - point number, occupying 1 bit (0 for positive, 1 for negative)
|
|
uint32_t sign : 1;
|
|
};
|
|
} double_u;
|
|
|
|
/**
|
|
* \brief Definition of the 'floatl2' structure.
|
|
*
|
|
* This structure is designed to represent a long - bit floating - point number in multiple ways.
|
|
* It uses a union to provide different views of the same underlying data,
|
|
* which can be accessed as arrays of 16 - bit or 32 - bit unsigned integers,
|
|
* or as a combination of two 'floatl' structures.
|
|
*/
|
|
typedef struct {
|
|
/**
|
|
* \brief Union to provide different representations of the underlying data.
|
|
*
|
|
* The union allows the data to be accessed in various formats:
|
|
* as an array of 16 - bit unsigned integers, an array of 32 - bit unsigned integers,
|
|
* or as two 'floatl' structures representing low and high parts.
|
|
*/
|
|
union {
|
|
/**
|
|
* \brief Array of uint16_t values representing the long bit integer in 16 - bit segments.
|
|
*
|
|
* This array can be used to access the underlying data in 16 - bit chunks.
|
|
* The size of the array is determined by the macro __FLOATL2_U16_PARTS__.
|
|
*/
|
|
uint16_t u16[__FLOATL2_U16_PARTS__];
|
|
|
|
/**
|
|
* \brief Array of uint32_t values representing the long bit integer in 32 - bit segments.
|
|
*
|
|
* This array can be used to access the underlying data in 32 - bit chunks.
|
|
* The size of the array is determined by the macro __FLOATL2_U32_PARTS__.
|
|
*/
|
|
uint32_t u32[__FLOATL2_U32_PARTS__];
|
|
|
|
/**
|
|
* \brief Structured view with low and high 'floatl' components.
|
|
*
|
|
* This struct divides the long - bit floating - point number into a low - order and a high - order 'floatl' part.
|
|
*/
|
|
struct {
|
|
/**
|
|
* \brief The low - order 'floatl' part of the long - bit floating - point number.
|
|
*/
|
|
floatl low;
|
|
|
|
/**
|
|
* \brief The high - order 'floatl' part of the long - bit floating - point number.
|
|
*/
|
|
floatl high;
|
|
};
|
|
};
|
|
} floatl2;
|
|
|
|
/**
|
|
* \brief Definition of the FL_STACK structure.
|
|
*
|
|
* This structure represents a stack data structure. It contains information
|
|
* about the stack's base address, capacity, current size, and the index of the stack tail.
|
|
*/
|
|
typedef struct
|
|
{
|
|
/**
|
|
* \brief Protected: base address of data.
|
|
*
|
|
* This pointer points to the base address of the memory area where the stack data is stored.
|
|
*/
|
|
char* base;
|
|
|
|
/**
|
|
* \brief Protected: capacity of stack.
|
|
*
|
|
* It indicates the maximum number of elements that the stack can hold.
|
|
*/
|
|
int capacity;
|
|
|
|
/**
|
|
* \brief Protected: size of stack.
|
|
*
|
|
* This variable stores the current number of elements in the stack.
|
|
*/
|
|
int size;
|
|
|
|
/**
|
|
* \brief Protected: index of stack tail.
|
|
*
|
|
* It represents the index where the next element will be pushed or the last element was popped.
|
|
*/
|
|
int tail;
|
|
} FL_STACK;
|
|
|
|
/**
|
|
* \brief Macro to create a 'floatl2' structure with a given low - order part and a zero high - order part.
|
|
* \param x: The value to be assigned to the 'low' member of the 'floatl2' structure.
|
|
* \return A 'floatl2' structure with the 'low' member set to 'x' and the 'high' member set to '__FLOATL_INT_ZERO__'.
|
|
*
|
|
* This macro simplifies the creation of a 'floatl2' structure by providing a convenient way to initialize
|
|
* the 'low' member while setting the 'high' member to a predefined zero value.
|
|
*/
|
|
#define FLOATL2(x) (floatl2){.low=(x),.high=__FLOATL_INT_ZERO__}
|
|
|
|
/**
|
|
* \brief Flag indicating zero - padding for formatted output.
|
|
*
|
|
* When this flag is set, the output will be padded with zeros instead of spaces when the width is specified.
|
|
*/
|
|
#define FLAGS_ZEROPAD (0x01)
|
|
|
|
/**
|
|
* \brief Flag indicating left - alignment for formatted output.
|
|
*
|
|
* When this flag is set, the output will be left - aligned within the specified width.
|
|
*/
|
|
#define FLAGS_LEFT (0x02)
|
|
|
|
/**
|
|
* \brief Flag indicating that a plus sign should be prepended to positive numbers in formatted output.
|
|
*
|
|
* When this flag is set, positive numbers will be prefixed with a '+' sign.
|
|
*/
|
|
#define FLAGS_PLUS (0x04)
|
|
|
|
/**
|
|
* \brief Flag indicating that a space should be prepended to positive numbers in formatted output.
|
|
*
|
|
* When this flag is set, positive numbers will be prefixed with a space.
|
|
*/
|
|
#define FLAGS_SPACE (0x08)
|
|
|
|
/**
|
|
* \brief Flag indicating the use of an alternate form for formatted output.
|
|
*
|
|
* The exact meaning of the alternate form depends on the specific format specifier.
|
|
*/
|
|
#define FLAGS_HASH (0x10)
|
|
|
|
/**
|
|
* \brief Flag indicating uppercase output for certain format specifiers.
|
|
*
|
|
* When this flag is set, format specifiers that support case (e.g., 'a' vs 'A') will use the uppercase form.
|
|
*/
|
|
#define FLAGS_CASE (0x20)
|
|
|
|
/**
|
|
* \brief Macro to print a character to the output buffer.
|
|
* \param c: The character to be printed.
|
|
*
|
|
* This macro checks if there is enough space in the buffer to store the character.
|
|
* If there is, it adds the character to the buffer and increments the length.
|
|
* If not, it returns - 2 to indicate that the buffer is too small.
|
|
*/
|
|
#define PRINT_CHAR(c) do { if (length < max) { buffer[length++] = (c); } else return -2; } while (0)
|
|
|
|
/**
|
|
* \brief An array storing powers of 10 from 10^0 to 10^15 in 'floatl' format.
|
|
*
|
|
* Each element in this array represents a power of 10, where the index corresponds to the exponent.
|
|
* For example, flpow10[0] is 10^0, flpow10[1] is 10^1, and so on up to flpow10[15] which is 10^15.
|
|
*/
|
|
static floatl flpow10[16] = {
|
|
FLOATL_CONST_1e0 , ///< Represents 10^0
|
|
FLOATL_CONST_1e1 , ///< Represents 10^1
|
|
FLOATL_CONST_1e2 , ///< Represents 10^2
|
|
FLOATL_CONST_1e3 , ///< Represents 10^3
|
|
FLOATL_CONST_1e4 , ///< Represents 10^4
|
|
FLOATL_CONST_1e5 , ///< Represents 10^5
|
|
FLOATL_CONST_1e6 , ///< Represents 10^6
|
|
FLOATL_CONST_1e7 , ///< Represents 10^7
|
|
FLOATL_CONST_1e8 , ///< Represents 10^8
|
|
FLOATL_CONST_1e9 , ///< Represents 10^9
|
|
FLOATL_CONST_1e10, ///< Represents 10^10
|
|
FLOATL_CONST_1e11, ///< Represents 10^11
|
|
FLOATL_CONST_1e12, ///< Represents 10^12
|
|
FLOATL_CONST_1e13, ///< Represents 10^13
|
|
FLOATL_CONST_1e14, ///< Represents 10^14
|
|
FLOATL_CONST_1e15, ///< Represents 10^15
|
|
};
|
|
|
|
/* Internal static function declarations */
|
|
|
|
static int count_u32_leading_zero(uint32_t x);
|
|
static floatl floatl_pow10(int32_t n);
|
|
static char hexI(char c);
|
|
static int pad_mant(floatl *number, int *part, int *bitoffset, int *bitnum, char c);
|
|
|
|
static int floatl_int_ucmp(floatl a, floatl b);
|
|
static floatl floatl_int_umul(floatl a, floatl b);
|
|
static floatl floatl_int_udiv(floatl a, floatl b, floatl *mod);
|
|
static floatl floatl_int_umod(floatl a, floatl b);
|
|
static floatl floatl_int_add(floatl a, floatl b);
|
|
static floatl floatl_int_sub(floatl a, floatl b);
|
|
static floatl floatl_int_inc(floatl a);
|
|
static floatl floatl_int_dec(floatl a);
|
|
static floatl floatl_int_mul(floatl a, floatl b);
|
|
static floatl floatl_int_div(floatl a, floatl b);
|
|
static floatl floatl_int_mod(floatl a, floatl b);
|
|
static floatl floatl_int_shl(floatl a, uint32_t amount);
|
|
static floatl floatl_int_shr(floatl a, uint32_t amount);
|
|
static floatl floatl_int_and(floatl a, floatl b);
|
|
static floatl floatl_int_or(floatl a, floatl b);
|
|
static floatl floatl_int_xor(floatl a, floatl b);
|
|
static floatl floatl_int_not(floatl a);
|
|
static floatl floatl_int_abs(floatl a);
|
|
static int floatl_int_sign(floatl a);
|
|
static int floatl_int_cmp(floatl a, floatl b);
|
|
static floatl floatl_int_neg(floatl a);
|
|
|
|
static int floatl2_int_ucmp(floatl2 a, floatl2 b);
|
|
static floatl2 floatl2_int_shr(floatl2 a, uint32_t amount);
|
|
static floatl2 floatl2_int_shl(floatl2 a, uint32_t amount);
|
|
static int floatl2_int_sign(floatl2 a);
|
|
static floatl2 floatl2_int_add(floatl2 a, floatl2 b);
|
|
static floatl2 floatl2_int_sub(floatl2 a, floatl2 b);
|
|
static floatl2 floatl2_int_umul(floatl2 a, floatl2 b);
|
|
static floatl2 floatl2_int_udiv(floatl2 a, floatl2 b, floatl2 *mod);
|
|
|
|
/**
|
|
* \brief Push a character onto the stack.
|
|
* \param[in,out] st: Pointer to the FL_STACK structure.
|
|
* \param[in] c: The character to be pushed onto the stack.
|
|
* \return 1 if the push operation is successful, always returns 1 in this implementation.
|
|
*
|
|
* This function adds a character to the stack. If the stack is not full, it increments the size.
|
|
* It then updates the tail index in a circular manner.
|
|
*/
|
|
static int fl_stack_push(FL_STACK *st, char c)
|
|
{
|
|
// Store the character at the current tail position
|
|
st->base[st->tail] = c;
|
|
// If the stack is not full, increment the size
|
|
if (st->size < st->capacity) st->size++;
|
|
// Update the tail index in a circular manner
|
|
st->tail = (st->tail + 1) % st->capacity;
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* \brief Pop a character from the stack.
|
|
* \param[in,out] st: Pointer to the FL_STACK structure.
|
|
* \param[out] c: Pointer to a character where the popped value will be stored.
|
|
* \return 1 if the pop operation is successful, 0 if the stack is empty.
|
|
*
|
|
* This function removes a character from the stack. It first checks if the stack is empty.
|
|
* If not, it updates the tail index in a circular manner, retrieves the character, and decrements the size.
|
|
*/
|
|
static int fl_stack_pop(FL_STACK *st, char *c)
|
|
{
|
|
// Check if the stack is empty
|
|
if (st->size == 0) return 0;
|
|
// Update the tail index in a circular manner to point to the last element
|
|
st->tail = (st->tail + st->capacity - 1) % st->capacity;
|
|
// Retrieve the character at the updated tail position
|
|
*c = st->base[st->tail];
|
|
// Decrement the size of the stack
|
|
st->size--;
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* \brief Push a character onto the stack from the head (a special push operation).
|
|
* \param[in,out] st: Pointer to the FL_STACK structure.
|
|
* \param[in] c: The character to be pushed onto the stack.
|
|
* \return 1 if the push operation is successful, 0 if the stack is full.
|
|
*
|
|
* This function adds a character to the stack from the "head" position.
|
|
* It first checks if the stack is full. If not, it calculates the appropriate position
|
|
* to insert the character and then increments the size.
|
|
*/
|
|
static int fl_stack_push_h(FL_STACK *st, char c)
|
|
{
|
|
// Check if the stack is full
|
|
if (st->size == st->capacity) return 0;
|
|
// Calculate the position to insert the character from the head
|
|
st->base[(st->tail - st->size + st->capacity - 1) % st->capacity] = c;
|
|
// Increment the size of the stack
|
|
st->size++;
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* \brief Adds two floatl numbers.
|
|
*
|
|
* This function computes the sum of two 128-bit integers
|
|
* (floatl). It processes each 16-bit part of the input integers,
|
|
* handling carry bits as necessary. The result is stored in a
|
|
* new floatl number. This function ensures that overflow is
|
|
* correctly managed across all parts.
|
|
*
|
|
* \param[in] a: The first operand (floatl number).
|
|
* \param[in] b: The second operand (floatl number).
|
|
* \return The sum of a and b as an floatl (128-bit integer).
|
|
*/
|
|
static floatl floatl_int_add(floatl a, floatl b)
|
|
{
|
|
floatl result; // Initialize the result variable
|
|
uint16_t carry = 0; /** Carry bit */
|
|
|
|
// Perform addition for each 16-bit part
|
|
for (int i = 0; i < __FLOATL_U16_PARTS__; i++)
|
|
{
|
|
// Calculate the sum of corresponding parts and carry
|
|
uint32_t sum = (uint32_t)a.u16[i] + (uint32_t)b.u16[i] + carry;
|
|
result.u16[i] = (uint16_t)(sum & 0xFFFF); /** Lower 16 bits */
|
|
carry = (sum >> 16) & 0xFFFF; /** Upper 16 bits as carry */
|
|
}
|
|
|
|
return result; // Return the resulting floatl number
|
|
}
|
|
|
|
/**
|
|
* \brief Subtracts one floatl number from another.
|
|
*
|
|
* This function computes the difference of two 128-bit integers
|
|
* (floatl). It processes each 16-bit part of the minuend and
|
|
* subtrahend, handling borrow bits as necessary. The result is
|
|
* stored in a new floatl number. This function ensures that
|
|
* borrowing is correctly managed across all parts.
|
|
*
|
|
* \param[in] a: The minuend (the number from which another is to be subtracted).
|
|
* \param[in] b: The subtrahend (the number to be subtracted).
|
|
* \return The result of a - b as an floatl (128-bit integer).
|
|
*/
|
|
static floatl floatl_int_sub(floatl a, floatl b)
|
|
{
|
|
floatl result; // Initialize the result variable
|
|
|
|
// Perform subtraction for each 16-bit part
|
|
for (int i = 0; i < __FLOATL_U16_PARTS__; i++)
|
|
{
|
|
uint32_t diff = (uint32_t)a.u16[i] - (uint32_t)b.u16[i];
|
|
|
|
// Check if a borrow occurred
|
|
if (diff & 0xFFFF0000) /** Borrow occurred */
|
|
{
|
|
// Adjust the higher parts to account for the borrow
|
|
for (int j = i + 1; j < __FLOATL_U16_PARTS__; j++)
|
|
{
|
|
a.u16[j] -= 1; // Borrow from the next part
|
|
if (a.u16[j] != 0xFFFF) break; // Stop if no further borrow needed
|
|
}
|
|
}
|
|
|
|
// Store the result of the subtraction
|
|
result.u16[i] = (uint16_t)(diff & 0xFFFF);
|
|
}
|
|
|
|
return result; // Return the resulting floatl number
|
|
}
|
|
|
|
/**
|
|
* \brief Increments the floatl number by one.
|
|
*
|
|
* This function increments a 128-bit integer (floatl) by one.
|
|
* It processes each 32-bit part of the input integer and
|
|
* handles carry bits as necessary. The function continues
|
|
* to increment the subsequent parts until there is no overflow.
|
|
*
|
|
* \param[in] a: The floatl number to increment.
|
|
* \return The incremented floatl number as an floatl (128-bit integer).
|
|
*/
|
|
static floatl floatl_int_inc(floatl a)
|
|
{
|
|
// Increment each 32-bit part of the floatl number
|
|
for (int i = 0; i < __FLOATL_U32_PARTS__; i++)
|
|
{
|
|
a.u32[i]++; // Increment the current part
|
|
// Check if the current part overflowed
|
|
if (a.u32[i] != 0)
|
|
{
|
|
break; /** Return immediately if no overflow */
|
|
}
|
|
}
|
|
return a; /** Return the incremented result */
|
|
}
|
|
|
|
/**
|
|
* \brief Decrements the floatl number by one.
|
|
*
|
|
* This function decrements a 128-bit integer (floatl) by one.
|
|
* It processes each 32-bit part of the input integer, handling
|
|
* borrowing as necessary. If the current part is zero, it sets
|
|
* that part to its maximum value (0xFFFFFFFF) and continues
|
|
* to the next part to borrow from it.
|
|
*
|
|
* \param[in] a: The floatl number to decrement.
|
|
* \return The decremented floatl number as an floatl (128-bit integer).
|
|
*/
|
|
static floatl floatl_int_dec(floatl a)
|
|
{
|
|
// Decrement each 32-bit part of the floatl number
|
|
for (int i = 0; i < __FLOATL_U32_PARTS__; i++)
|
|
{
|
|
// Check if the current part can be decremented
|
|
if (a.u32[i] != 0)
|
|
{
|
|
a.u32[i]--; // Decrement the current part
|
|
break; /** Return immediately if current part can be decremented */
|
|
}
|
|
// If current part is zero, set it to max value and continue borrowing
|
|
a.u32[i] = 0xFFFFFFFF;
|
|
}
|
|
return a; /** Return the decremented result */
|
|
}
|
|
|
|
/**
|
|
* \brief Multiplies two floatl unsigned numbers.
|
|
*
|
|
* This function performs multiplication of two 128-bit unsigned integers
|
|
* (floatl) by using a method similar to the schoolbook algorithm. The
|
|
* multiplication is carried out by breaking the numbers into their
|
|
* 16-bit components and accumulating the results. The function handles
|
|
* carry-over during the multiplication and addition stages to ensure
|
|
* the final product is accurately represented.
|
|
*
|
|
* \param[in] a: The first operand to multiply.
|
|
* \param[in] b: The second operand to multiply.
|
|
* \return The product of a and b as an floatl (128-bit unsigned integer).
|
|
*/
|
|
static floatl floatl_int_umul(floatl a, floatl b)
|
|
{
|
|
floatl result = {0}; /** Initialize the result to 0 */
|
|
floatl temp[__FLOATL_U16_PARTS__] = {{0}}; // Temporary storage for intermediate results
|
|
uint16_t carry = 0; // Variable to hold carry-over during multiplication
|
|
|
|
// Perform multiplication
|
|
for (int i = 0; i < __FLOATL_U16_PARTS__; i++)
|
|
{
|
|
carry = 0; // Reset carry for the current row
|
|
for (int j = 0; j < __FLOATL_U16_PARTS__; j++)
|
|
{
|
|
if (i + j < __FLOATL_U16_PARTS__)
|
|
{
|
|
// Multiply the 16-bit segments and add carry
|
|
uint32_t mul = (uint32_t)a.u16[i] * (uint32_t)b.u16[j] + carry;
|
|
temp[i].u16[i + j] = (mul & 0xFFFF); // Store the lower 16 bits
|
|
carry = ((mul >> 16) & 0xFFFF); // Update carry for the next addition
|
|
}
|
|
}
|
|
}
|
|
|
|
carry = 0; // Reset carry for the addition phase
|
|
// Combine results from the temporary storage
|
|
for (int i = 0; i < __FLOATL_U16_PARTS__; i++)
|
|
{
|
|
uint32_t add = 0; // Variable to hold the sum of the current column
|
|
for (int j = 0; j < __FLOATL_U16_PARTS__; j++)
|
|
{
|
|
add += temp[j].u16[i]; // Accumulate results from temp
|
|
}
|
|
add += carry; // Add any carry from the previous column
|
|
result.u16[i] = (add & 0xFFFF); // Store the lower 16 bits in result
|
|
carry = ((add >> 16) & 0xFFFF); // Update carry for the next column
|
|
}
|
|
|
|
return result; // Return the final product
|
|
}
|
|
|
|
/**
|
|
* \brief Multiplies two floatl numbers.
|
|
*
|
|
* This function multiplies two 128-bit integers (floatl) and returns
|
|
* the product as another floatl number. It handles signed multiplication
|
|
* by checking the sign of the operands. If either operand is negative,
|
|
* it negates the operand and adjusts the sign of the result accordingly.
|
|
* The actual multiplication is performed using the `floatl_int_umul`
|
|
* function, which handles the absolute values of the integers.
|
|
*
|
|
* \param[in] a: The first operand to multiply.
|
|
* \param[in] b: The second operand to multiply.
|
|
* \return The product of a and b as an floatl (128-bit integer).
|
|
*/
|
|
static floatl floatl_int_mul(floatl a, floatl b)
|
|
{
|
|
floatl result = {0}; // Initialize the result to 0
|
|
int sign = 1; // Variable to track the sign of the result
|
|
|
|
// Check and handle the sign of the first operand
|
|
if (a.u32[__FLOATL_U32_PARTS__ - 1] & 0x80000000)
|
|
{
|
|
sign = -sign; // Negate the sign for the result
|
|
a = floatl_int_neg(a); // Negate the first operand
|
|
}
|
|
|
|
// Check and handle the sign of the second operand
|
|
if (b.u32[__FLOATL_U32_PARTS__ - 1] & 0x80000000)
|
|
{
|
|
sign = -sign; // Negate the sign for the result
|
|
b = floatl_int_neg(b); // Negate the second operand
|
|
}
|
|
|
|
// Perform unsigned multiplication
|
|
result = floatl_int_umul(a, b);
|
|
|
|
// If the result should be negative, negate it
|
|
if (sign < 0)
|
|
result = floatl_int_neg(result);
|
|
|
|
return result; // Return the final product
|
|
}
|
|
|
|
/**
|
|
* \brief Divides one floatl unsigned number by another.
|
|
*
|
|
* This function performs division of one 128-bit unsigned integer (floatl)
|
|
* by another. It calculates the quotient using a bitwise approach,
|
|
* handling division by zero gracefully. The result is built bit by bit
|
|
* from the most significant bit to the least significant bit. If the
|
|
* divisor is zero, it prints an error message and returns zero.
|
|
*
|
|
* \param[in] a: The dividend (number to be divided).
|
|
* \param[in] b: The divisor (number to divide by).
|
|
* \return The quotient of a divided by b as an floatl (128-bit unsigned integer).
|
|
*/
|
|
static floatl floatl_int_udiv(floatl a, floatl b, floatl *mod)
|
|
{
|
|
// Check for division by zero
|
|
if (floatl_int_sign(b) == 0)
|
|
{
|
|
printf("Division by zero!\n");
|
|
return (floatl){0}; /** Handle division by zero */
|
|
}
|
|
|
|
floatl result = {0}; // Initialize the result to zero
|
|
floatl remainder = {0}; // Initialize the remainder to zero
|
|
|
|
/** Calculate bit by bit from the highest bit */
|
|
for (int i = __FLOATL_BIT_PARTS__ - 1; i >= 0; i--)
|
|
{
|
|
/** Left shift remainder and add current bit */
|
|
remainder = floatl_int_shl(remainder, 1); // Shift remainder left by 1
|
|
remainder.u32[0] |= (a.u32[i / 32] >> (i % 32)) & 1; // Add current bit from dividend
|
|
|
|
/** If remainder is greater than or equal to b, subtract b */
|
|
if (floatl_int_ucmp(remainder, b) >= 0)
|
|
{
|
|
remainder = floatl_int_sub(remainder, b); // Subtract b from remainder
|
|
result.u32[i / 32] |= (1 << (i % 32)); // Set corresponding bit in result
|
|
}
|
|
}
|
|
|
|
if (mod) *mod = remainder;
|
|
|
|
return result; // Return the final quotient
|
|
}
|
|
|
|
/**
|
|
* \brief Divides one floatl number by another.
|
|
*
|
|
* This function performs division of two 128-bit integers (floatl)
|
|
* and returns the quotient as another floatl number. It handles signed
|
|
* division by checking the sign of the operands. If either operand
|
|
* is negative, it negates the operand and adjusts the sign of the
|
|
* result accordingly. The actual division is performed using the
|
|
* `floatl_int_udiv` function, which handles the absolute values of
|
|
* the integers.
|
|
*
|
|
* \param[in] a: The dividend (number to be divided).
|
|
* \param[in] b: The divisor (number to divide by).
|
|
* \return The quotient of a divided by b as an floatl (128-bit integer).
|
|
*/
|
|
static floatl floatl_int_div(floatl a, floatl b)
|
|
{
|
|
floatl result = {0}; // Initialize the result to 0
|
|
int sign = 1; // Variable to track the sign of the result
|
|
|
|
// Check and handle the sign of the dividend
|
|
if (a.u32[__FLOATL_U32_PARTS__ - 1] & 0x80000000)
|
|
{
|
|
sign = -sign; // Negate the sign for the result
|
|
a = floatl_int_neg(a); // Negate the dividend
|
|
}
|
|
|
|
// Check and handle the sign of the divisor
|
|
if (b.u32[__FLOATL_U32_PARTS__ - 1] & 0x80000000)
|
|
{
|
|
sign = -sign; // Negate the sign for the result
|
|
b = floatl_int_neg(b); // Negate the divisor
|
|
}
|
|
|
|
// Perform unsigned division
|
|
result = floatl_int_udiv(a, b, NULL);
|
|
|
|
// If the result should be negative, negate it
|
|
if (sign < 0)
|
|
result = floatl_int_neg(result);
|
|
|
|
return result; // Return the final quotient
|
|
}
|
|
|
|
/**
|
|
* \brief Computes the remainder of the division of two unsigned floatl numbers.
|
|
*
|
|
* This function calculates the remainder of the division of two
|
|
* 128-bit unsigned integers (floatl). It uses a bitwise approach to
|
|
* compute the remainder by processing each bit from the most significant
|
|
* to the least significant. If the divisor is zero, it handles the
|
|
* error gracefully by printing a message and returning zero.
|
|
*
|
|
* \param[in] a: The dividend (number to be divided).
|
|
* \param[in] b: The divisor (number to divide by).
|
|
* \return The remainder of a divided by b as an floatl (128-bit unsigned integer).
|
|
*/
|
|
static floatl floatl_int_umod(floatl a, floatl b)
|
|
{
|
|
floatl mod = {0};
|
|
floatl_int_udiv(a, b, &mod);
|
|
return mod;
|
|
}
|
|
|
|
/**
|
|
* \brief Computes the remainder of the division of two floatl numbers.
|
|
*
|
|
* This function calculates the remainder of the division of two
|
|
* 128-bit integers (floatl). It handles signed integers by checking
|
|
* the sign of the dividend. If the dividend is negative, it negates
|
|
* the dividend before performing the unsigned modulus operation.
|
|
* The sign of the result is adjusted based on the sign of the
|
|
* dividend. The actual remainder calculation is performed using the
|
|
* `floatl_int_umod` function, which handles the absolute values of
|
|
* the integers.
|
|
*
|
|
* \param[in] a: The dividend (number to be divided).
|
|
* \param[in] b: The divisor (number to divide by).
|
|
* \return The remainder of a divided by b as an floatl (128-bit integer).
|
|
*/
|
|
static floatl floatl_int_mod(floatl a, floatl b)
|
|
{
|
|
floatl result = {0}; // Initialize result to zero
|
|
int sign = 1; // Variable to track the sign of the result
|
|
|
|
// Check and handle the sign of the dividend
|
|
if (a.u32[__FLOATL_U32_PARTS__ - 1] & 0x80000000)
|
|
{
|
|
sign = -sign; // Negate the sign for the result
|
|
a = floatl_int_neg(a); // Negate the dividend
|
|
}
|
|
|
|
// Perform unsigned modulus with the absolute value of the divisor
|
|
result = floatl_int_umod(a, floatl_int_abs(b));
|
|
|
|
// If the result should be negative, negate it
|
|
if (sign < 0)
|
|
result = floatl_int_neg(result);
|
|
|
|
return result; // Return the final remainder
|
|
}
|
|
|
|
/**
|
|
* \brief Left shifts an floatl number by a specified number of bits.
|
|
*
|
|
* This function performs a left bitwise shift on a 128-bit integer
|
|
* (floatl). The shift amount can be greater than 32 bits, in which case
|
|
* the function calculates how many whole 32-bit parts to shift and
|
|
* how many bits to shift within the remaining part. It constructs
|
|
* the result based on the input number after applying the shift.
|
|
*
|
|
* \param[in] a: The floatl number to shift.
|
|
* \param[in] amount: The number of bits to shift to the left.
|
|
* \return The left-shifted floatl number.
|
|
*/
|
|
static floatl floatl_int_shl(floatl a, uint32_t amount)
|
|
{
|
|
floatl result = {0}; // Initialize the result to zero
|
|
int u32bias = amount / 32; // Number of whole 32-bit parts to shift
|
|
int bitsbias = amount % 32; // Remaining bits to shift
|
|
|
|
// Perform the shift for each 32-bit part of the floatl number
|
|
for (int i = 0; i < __FLOATL_U32_PARTS__; i++)
|
|
{
|
|
if (i < u32bias)
|
|
{
|
|
result.u32[i] = 0; // Set shifted-out parts to zero
|
|
}
|
|
else
|
|
{
|
|
// Shift the current part and add bits from the previous part if needed
|
|
result.u32[i] = (a.u32[i - u32bias] << bitsbias) |
|
|
(((i - u32bias - 1) >= 0 && bitsbias > 0) ?
|
|
(a.u32[i - u32bias - 1] >> (32 - bitsbias)) : 0);
|
|
}
|
|
}
|
|
return result; // Return the left-shifted result
|
|
}
|
|
|
|
/**
|
|
* \brief Right shifts an floatl number by a specified number of bits.
|
|
*
|
|
* This function performs a right bitwise shift on a 128-bit integer
|
|
* (floatl). The shift amount can be greater than 32 bits, in which case
|
|
* the function calculates how many whole 32-bit parts to shift and
|
|
* how many bits to shift within the remaining part. It constructs
|
|
* the result based on the input number after applying the shift.
|
|
* The sign bit is preserved for signed shifts.
|
|
*
|
|
* \param[in] a: The floatl number to shift.
|
|
* \param[in] amount: The number of bits to shift to the right.
|
|
* \return The right-shifted floatl number.
|
|
*/
|
|
static floatl floatl_int_shr(floatl a, uint32_t amount)
|
|
{
|
|
floatl result = {0}; // Initialize the result to zero
|
|
int u32bias = amount / 32; // Number of whole 32-bit parts to shift
|
|
int bitsbias = amount % 32; // Remaining bits to shift
|
|
|
|
// Perform the shift for each 32-bit part of the floatl number
|
|
for (int i = 0; i < __FLOATL_U32_PARTS__; i++)
|
|
{
|
|
// Check if the current index is beyond the range for valid shifts
|
|
if (i > __FLOATL_U32_PARTS__ - u32bias - 1 && __FLOATL_U32_PARTS__ - u32bias - 1 >= 0)
|
|
{
|
|
result.u32[i] = 0; // Set shifted-out parts to zero
|
|
}
|
|
else
|
|
{
|
|
// Shift the current part and add bits from the next part if needed
|
|
result.u32[i] = (a.u32[i + u32bias] >> bitsbias) |
|
|
(((i + u32bias + 1) < __FLOATL_U32_PARTS__ && bitsbias > 0) ?
|
|
(a.u32[i + u32bias + 1] << (32 - bitsbias)) :
|
|
((a.u32[__FLOATL_U32_PARTS__ - 1] & 0x80000000) ? 0xFFFFFFFF : 0));
|
|
}
|
|
}
|
|
|
|
return result; // Return the right-shifted result
|
|
}
|
|
|
|
/**
|
|
* \brief Performs bitwise AND operation on two floatl numbers.
|
|
*
|
|
* This function computes the bitwise AND of two 128-bit integers
|
|
* (floatl). It processes each 32-bit part of the input integers and
|
|
* performs the AND operation on corresponding parts, storing the
|
|
* result in a new floatl number. This operation yields a number that
|
|
* has bits set only where both operands have bits set.
|
|
*
|
|
* \param[in] a: The first operand (floatl number).
|
|
* \param[in] b: The second operand (floatl number).
|
|
* \return The result of a AND b as an floatl (128-bit integer).
|
|
*/
|
|
static floatl floatl_int_and(floatl a, floatl b)
|
|
{
|
|
floatl result; // Initialize the result variable
|
|
// Perform the bitwise AND operation for each 32-bit part
|
|
for (int i = 0; i < __FLOATL_U32_PARTS__; i++)
|
|
{
|
|
result.u32[i] = a.u32[i] & b.u32[i]; // Compute AND for each part
|
|
}
|
|
return result; // Return the resulting floatl number
|
|
}
|
|
|
|
/**
|
|
* \brief Performs bitwise OR operation on two floatl numbers.
|
|
*
|
|
* This function computes the bitwise OR of two 128-bit integers
|
|
* (floatl). It processes each 32-bit part of the input integers and
|
|
* performs the OR operation on corresponding parts, storing the
|
|
* result in a new floatl number. This operation yields a number that
|
|
* has bits set where at least one of the operands has bits set.
|
|
*
|
|
* \param[in] a: The first operand (floatl number).
|
|
* \param[in] b: The second operand (floatl number).
|
|
* \return The result of a OR b as an floatl (128-bit integer).
|
|
*/
|
|
static floatl floatl_int_or(floatl a, floatl b)
|
|
{
|
|
floatl result; // Initialize the result variable
|
|
// Perform the bitwise OR operation for each 32-bit part
|
|
for (int i = 0; i < __FLOATL_U32_PARTS__; i++)
|
|
{
|
|
result.u32[i] = a.u32[i] | b.u32[i]; // Compute OR for each part
|
|
}
|
|
return result; // Return the resulting floatl number
|
|
}
|
|
|
|
/**
|
|
* \brief Performs bitwise XOR operation on two floatl numbers.
|
|
*
|
|
* This function computes the bitwise XOR of two 128-bit integers
|
|
* (floatl). It processes each 32-bit part of the input integers and
|
|
* performs the XOR operation on corresponding parts, storing the
|
|
* result in a new floatl number. This operation yields a number that
|
|
* has bits set where only one of the operands has bits set.
|
|
*
|
|
* \param[in] a: The first operand (floatl number).
|
|
* \param[in] b: The second operand (floatl number).
|
|
* \return The result of a XOR b as an floatl (128-bit integer).
|
|
*/
|
|
static floatl floatl_int_xor(floatl a, floatl b)
|
|
{
|
|
floatl result; // Initialize the result variable
|
|
// Perform the bitwise XOR operation for each 32-bit part
|
|
for (int i = 0; i < __FLOATL_U32_PARTS__; i++)
|
|
{
|
|
result.u32[i] = a.u32[i] ^ b.u32[i]; // Compute XOR for each part
|
|
}
|
|
return result; // Return the resulting floatl number
|
|
}
|
|
|
|
/**
|
|
* \brief Performs bitwise NOT operation on an floatl number.
|
|
*
|
|
* This function computes the bitwise NOT (negation) of a 128-bit
|
|
* integer (floatl). It processes each 32-bit part of the input integer
|
|
* and applies the NOT operation, storing the result in a new floatl
|
|
* number. This operation inverts all bits of the input number.
|
|
*
|
|
* \param[in] a: The floatl number to negate.
|
|
* \return The bitwise negation of a as an floatl (128-bit integer).
|
|
*/
|
|
static floatl floatl_int_not(floatl a)
|
|
{
|
|
floatl result; // Initialize the result variable
|
|
// Perform the bitwise NOT operation for each 32-bit part
|
|
for (int i = 0; i < __FLOATL_U32_PARTS__; i++)
|
|
{
|
|
result.u32[i] = ~a.u32[i]; // Compute NOT for each part
|
|
}
|
|
return result; // Return the resulting floatl number
|
|
}
|
|
|
|
/**
|
|
* \brief Computes the absolute value of an floatl number.
|
|
*
|
|
* This function checks if the given 128-bit unsigned integer (floatl)
|
|
* represents a negative value in two's complement representation.
|
|
* If the most significant bit (sign bit) of the highest 32-bit segment
|
|
* is set, it indicates a negative number, and the function calls
|
|
* floatl_int_neg to return its positive equivalent. If the number is
|
|
* already non-negative, it simply returns the original number.
|
|
*
|
|
* \param[in] a: The floatl number for which to compute the absolute value.
|
|
* \return The absolute value of the floatl number a.
|
|
*/
|
|
static floatl floatl_int_abs(floatl a)
|
|
{
|
|
// Check if the sign bit of the highest 32-bit part is set
|
|
if (a.u32[__FLOATL_U32_PARTS__ - 1] & 0x80000000)
|
|
return floatl_int_neg(a); // Return negated value if negative
|
|
return a; // Return the original value if non-negative
|
|
}
|
|
|
|
/**
|
|
* \brief Determines the sign of an floatl number.
|
|
*
|
|
* This function checks the sign of the given 128-bit unsigned integer
|
|
* (floatl) based on its representation. It first examines the most significant
|
|
* bit of the highest 32-bit segment to determine if the number is negative.
|
|
* If this bit is set, the function returns -1, indicating a negative value.
|
|
* If all segments are zero, it returns 0, indicating that the number is zero.
|
|
* If the number is positive, it returns 1.
|
|
*
|
|
* \param[in] a: The floatl number to evaluate for its sign.
|
|
* \return -1 if the number is negative, 0 if the number is zero, and
|
|
* 1 if the number is positive.
|
|
*/
|
|
static int floatl_int_sign(floatl a)
|
|
{
|
|
// Check if the sign bit of the highest 32-bit part is set
|
|
if (a.u32[__FLOATL_U32_PARTS__ - 1] & 0x80000000) return -1;
|
|
|
|
// Check if the number is zero
|
|
for (int i = __FLOATL_U32_PARTS__ - 1; i >= 0; i--)
|
|
{
|
|
if (a.u32[i] != 0) return 1; // Return 1 if any part is non-zero
|
|
}
|
|
|
|
return 0; // Return 0 if all parts are zero
|
|
}
|
|
|
|
/**
|
|
* \brief Compares two floatl unsigned numbers.
|
|
*
|
|
* This function compares two 128-bit unsigned integers (floatl)
|
|
* by examining each 32-bit segment from the most significant to
|
|
* the least significant. It returns 1 if the first number is
|
|
* greater than the second, -1 if it is less, and 0 if they are
|
|
* equal. The comparison is done in a way that respects the
|
|
* unsigned nature of the integers.
|
|
*
|
|
* \param[in] a: The first number to compare.
|
|
* \param[in] b: The second number to compare.
|
|
* \return 1 if a > b, -1 if a < b, and 0 if a == b.
|
|
*/
|
|
static int floatl_int_ucmp(floatl a, floatl b)
|
|
{
|
|
// Compare each 32-bit part from the most significant to the least significant
|
|
for (int i = __FLOATL_U32_PARTS__ - 1; i >= 0; i--)
|
|
{
|
|
if (a.u32[i] > b.u32[i]) return 1; // a is greater
|
|
if (a.u32[i] < b.u32[i]) return -1; // a is less
|
|
}
|
|
return 0; // a and b are equal
|
|
}
|
|
|
|
/**
|
|
* \brief Compares two floatl numbers.
|
|
*
|
|
* This function compares two 128-bit integers (floatl) and
|
|
* determines their relative order. The comparison is performed
|
|
* starting from the most significant part (highest order) to
|
|
* the least significant part (lowest order).
|
|
*
|
|
* \param[in] a: The first number to compare.
|
|
* \param[in] b: The second number to compare.
|
|
*
|
|
* \return 1 if a > b, -1 if a < b, 0 if a == b.
|
|
*/
|
|
static int floatl_int_cmp(floatl a, floatl b)
|
|
{
|
|
// Compare each 32-bit part from the most significant to the least significant
|
|
for (int i = __FLOATL_U32_PARTS__ - 1; i >= 0; i--)
|
|
{
|
|
// Compare the current parts as signed integers
|
|
if ((int32_t)a.u32[i] > (int32_t)b.u32[i]) return 1; // a is greater
|
|
if ((int32_t)a.u32[i] < (int32_t)b.u32[i]) return -1; // a is less
|
|
}
|
|
return 0; // a is equal to b
|
|
}
|
|
|
|
/**
|
|
* \brief Computes the two's complement (negation) of an floatl number.
|
|
*
|
|
* This function calculates the negative representation of a given 128-bit
|
|
* unsigned integer (floatl) using two's complement. It first inverts all bits
|
|
* of the input number and then adds one to the result. This effectively
|
|
* represents the negative value of the original number in a signed
|
|
* integer format.
|
|
*
|
|
* \param[in] a: The floatl number to negate.
|
|
* \return The negated floatl number (two's complement of a).
|
|
*/
|
|
static floatl floatl_int_neg(floatl a)
|
|
{
|
|
floatl result = {0};
|
|
|
|
// First, bitwise NOT (invert) the input number
|
|
for (int i = 0; i < __FLOATL_U32_PARTS__; i++)
|
|
{
|
|
result.u32[i] = ~a.u32[i];
|
|
}
|
|
|
|
// Add one to complete the two's complement operation
|
|
return floatl_int_inc(result);
|
|
}
|
|
|
|
/**
|
|
* \brief Compares two floatl unsigned numbers.
|
|
*
|
|
* This function compares two 128-bit unsigned integers (floatl)
|
|
* by examining each 32-bit segment from the most significant to
|
|
* the least significant. It returns 1 if the first number is
|
|
* greater than the second, -1 if it is less, and 0 if they are
|
|
* equal. The comparison is done in a way that respects the
|
|
* unsigned nature of the integers.
|
|
*
|
|
* \param[in] a: The first number to compare.
|
|
* \param[in] b: The second number to compare.
|
|
* \return 1 if a > b, -1 if a < b, and 0 if a == b.
|
|
*/
|
|
static int floatl2_int_ucmp(floatl2 a, floatl2 b)
|
|
{
|
|
// Compare each 32-bit part from the most significant to the least significant
|
|
for (int i = __FLOATL2_U32_PARTS__ - 1; i >= 0; i--)
|
|
{
|
|
if (a.u32[i] > b.u32[i]) return 1; // a is greater
|
|
if (a.u32[i] < b.u32[i]) return -1; // a is less
|
|
}
|
|
return 0; // a and b are equal
|
|
}
|
|
|
|
/**
|
|
* \brief Right shifts an floatl number by a specified number of bits.
|
|
*
|
|
* This function performs a right bitwise shift on a 128-bit integer
|
|
* (floatl). The shift amount can be greater than 32 bits, in which case
|
|
* the function calculates how many whole 32-bit parts to shift and
|
|
* how many bits to shift within the remaining part. It constructs
|
|
* the result based on the input number after applying the shift.
|
|
* The sign bit is preserved for signed shifts.
|
|
*
|
|
* \param[in] a: The floatl number to shift.
|
|
* \param[in] amount: The number of bits to shift to the right.
|
|
* \return The right-shifted floatl number.
|
|
*/
|
|
static floatl2 floatl2_int_shr(floatl2 a, uint32_t amount)
|
|
{
|
|
floatl2 result = {0}; // Initialize the result to zero
|
|
int u32bias = amount / 32; // Number of whole 32-bit parts to shift
|
|
int bitsbias = amount % 32; // Remaining bits to shift
|
|
|
|
// Perform the shift for each 32-bit part of the floatl number
|
|
for (int i = 0; i < __FLOATL2_U32_PARTS__; i++)
|
|
{
|
|
// Check if the current index is beyond the range for valid shifts
|
|
if (i > __FLOATL2_U32_PARTS__ - u32bias - 1 && __FLOATL2_U32_PARTS__ - u32bias - 1 >= 0)
|
|
{
|
|
result.u32[i] = 0; // Set shifted-out parts to zero
|
|
}
|
|
else
|
|
{
|
|
// Shift the current part and add bits from the next part if needed
|
|
result.u32[i] = (a.u32[i + u32bias] >> bitsbias) |
|
|
(((i + u32bias + 1) < __FLOATL2_U32_PARTS__ && bitsbias > 0) ?
|
|
(a.u32[i + u32bias + 1] << (32 - bitsbias)) :
|
|
((a.u32[__FLOATL2_U32_PARTS__ - 1] & 0x80000000) ? 0xFFFFFFFF : 0));
|
|
}
|
|
}
|
|
|
|
return result; // Return the right-shifted result
|
|
}
|
|
|
|
/**
|
|
* \brief Left shifts an floatl number by a specified number of bits.
|
|
*
|
|
* This function performs a left bitwise shift on a 128-bit integer
|
|
* (floatl). The shift amount can be greater than 32 bits, in which case
|
|
* the function calculates how many whole 32-bit parts to shift and
|
|
* how many bits to shift within the remaining part. It constructs
|
|
* the result based on the input number after applying the shift.
|
|
*
|
|
* \param[in] a: The floatl number to shift.
|
|
* \param[in] amount: The number of bits to shift to the left.
|
|
* \return The left-shifted floatl number.
|
|
*/
|
|
static floatl2 floatl2_int_shl(floatl2 a, uint32_t amount)
|
|
{
|
|
floatl2 result = {0}; // Initialize the result to zero
|
|
int u32bias = amount / 32; // Number of whole 32-bit parts to shift
|
|
int bitsbias = amount % 32; // Remaining bits to shift
|
|
|
|
// Perform the shift for each 32-bit part of the floatl number
|
|
for (int i = 0; i < __FLOATL2_U32_PARTS__; i++)
|
|
{
|
|
if (i < u32bias)
|
|
{
|
|
result.u32[i] = 0; // Set shifted-out parts to zero
|
|
}
|
|
else
|
|
{
|
|
// Shift the current part and add bits from the previous part if needed
|
|
result.u32[i] = (a.u32[i - u32bias] << bitsbias) |
|
|
(((i - u32bias - 1) >= 0 && bitsbias > 0) ?
|
|
(a.u32[i - u32bias - 1] >> (32 - bitsbias)) : 0);
|
|
}
|
|
}
|
|
return result; // Return the left-shifted result
|
|
}
|
|
|
|
/**
|
|
* \brief Determines the sign of an floatl number.
|
|
*
|
|
* This function checks the sign of the given 128-bit unsigned integer
|
|
* (floatl) based on its representation. It first examines the most significant
|
|
* bit of the highest 32-bit segment to determine if the number is negative.
|
|
* If this bit is set, the function returns -1, indicating a negative value.
|
|
* If all segments are zero, it returns 0, indicating that the number is zero.
|
|
* If the number is positive, it returns 1.
|
|
*
|
|
* \param[in] a: The floatl number to evaluate for its sign.
|
|
* \return -1 if the number is negative, 0 if the number is zero, and
|
|
* 1 if the number is positive.
|
|
*/
|
|
static int floatl2_int_sign(floatl2 a)
|
|
{
|
|
// Check if the sign bit of the highest 32-bit part is set
|
|
if (a.u32[__FLOATL2_U32_PARTS__ - 1] & 0x80000000) return -1;
|
|
|
|
// Check if the number is zero
|
|
for (int i = __FLOATL2_U32_PARTS__ - 1; i >= 0; i--)
|
|
{
|
|
if (a.u32[i] != 0) return 1; // Return 1 if any part is non-zero
|
|
}
|
|
|
|
return 0; // Return 0 if all parts are zero
|
|
}
|
|
|
|
/**
|
|
* \brief Adds two floatl numbers.
|
|
*
|
|
* This function computes the sum of two 128-bit integers
|
|
* (floatl). It processes each 16-bit part of the input integers,
|
|
* handling carry bits as necessary. The result is stored in a
|
|
* new floatl number. This function ensures that overflow is
|
|
* correctly managed across all parts.
|
|
*
|
|
* \param[in] a: The first operand (floatl number).
|
|
* \param[in] b: The second operand (floatl number).
|
|
* \return The sum of a and b as an floatl (128-bit integer).
|
|
*/
|
|
static floatl2 floatl2_int_add(floatl2 a, floatl2 b)
|
|
{
|
|
floatl2 result; // Initialize the result variable
|
|
uint16_t carry = 0; /** Carry bit */
|
|
|
|
// Perform addition for each 16-bit part
|
|
for (int i = 0; i < __FLOATL2_U16_PARTS__; i++)
|
|
{
|
|
// Calculate the sum of corresponding parts and carry
|
|
uint32_t sum = (uint32_t)a.u16[i] + (uint32_t)b.u16[i] + carry;
|
|
result.u16[i] = (uint16_t)(sum & 0xFFFF); /** Lower 16 bits */
|
|
carry = (sum >> 16) & 0xFFFF; /** Upper 16 bits as carry */
|
|
}
|
|
|
|
return result; // Return the resulting floatl number
|
|
}
|
|
|
|
/**
|
|
* \brief Subtracts one floatl number from another.
|
|
*
|
|
* This function computes the difference of two 128-bit integers
|
|
* (floatl). It processes each 16-bit part of the minuend and
|
|
* subtrahend, handling borrow bits as necessary. The result is
|
|
* stored in a new floatl number. This function ensures that
|
|
* borrowing is correctly managed across all parts.
|
|
*
|
|
* \param[in] a: The minuend (the number from which another is to be subtracted).
|
|
* \param[in] b: The subtrahend (the number to be subtracted).
|
|
* \return The result of a - b as an floatl (128-bit integer).
|
|
*/
|
|
static floatl2 floatl2_int_sub(floatl2 a, floatl2 b)
|
|
{
|
|
floatl2 result; // Initialize the result variable
|
|
|
|
// Perform subtraction for each 16-bit part
|
|
for (int i = 0; i < __FLOATL2_U16_PARTS__; i++)
|
|
{
|
|
uint32_t diff = (uint32_t)a.u16[i] - (uint32_t)b.u16[i];
|
|
|
|
// Check if a borrow occurred
|
|
if (diff & 0xFFFF0000) /** Borrow occurred */
|
|
{
|
|
// Adjust the higher parts to account for the borrow
|
|
for (int j = i + 1; j < __FLOATL2_U16_PARTS__; j++)
|
|
{
|
|
a.u16[j] -= 1; // Borrow from the next part
|
|
if (a.u16[j] != 0xFFFF) break; // Stop if no further borrow needed
|
|
}
|
|
}
|
|
|
|
// Store the result of the subtraction
|
|
result.u16[i] = (uint16_t)(diff & 0xFFFF);
|
|
}
|
|
|
|
return result; // Return the resulting floatl number
|
|
}
|
|
|
|
/**
|
|
* \brief Multiplies two floatl unsigned numbers.
|
|
*
|
|
* This function performs multiplication of two 128-bit unsigned integers
|
|
* (floatl) by using a method similar to the schoolbook algorithm. The
|
|
* multiplication is carried out by breaking the numbers into their
|
|
* 16-bit components and accumulating the results. The function handles
|
|
* carry-over during the multiplication and addition stages to ensure
|
|
* the final product is accurately represented.
|
|
*
|
|
* \param[in] a: The first operand to multiply.
|
|
* \param[in] b: The second operand to multiply.
|
|
* \return The product of a and b as an floatl (128-bit unsigned integer).
|
|
*/
|
|
static floatl2 floatl2_int_umul(floatl2 a, floatl2 b)
|
|
{
|
|
floatl2 result = {0}; /** Initialize the result to 0 */
|
|
floatl2 temp[__FLOATL_U16_PARTS__*2] = {{0}}; // Temporary storage for intermediate results
|
|
uint16_t carry = 0; // Variable to hold carry-over during multiplication
|
|
|
|
// Perform multiplication
|
|
for (int i = 0; i < __FLOATL2_U16_PARTS__; i++)
|
|
{
|
|
carry = 0; // Reset carry for the current row
|
|
for (int j = 0; j < __FLOATL2_U16_PARTS__; j++)
|
|
{
|
|
if (i + j < __FLOATL2_U16_PARTS__)
|
|
{
|
|
// Multiply the 16-bit segments and add carry
|
|
uint32_t mul = (uint32_t)a.u16[i] * (uint32_t)b.u16[j] + carry;
|
|
temp[i].u16[i + j] = (mul & 0xFFFF); // Store the lower 16 bits
|
|
carry = ((mul >> 16) & 0xFFFF); // Update carry for the next addition
|
|
}
|
|
}
|
|
}
|
|
|
|
carry = 0; // Reset carry for the addition phase
|
|
// Combine results from the temporary storage
|
|
for (int i = 0; i < __FLOATL2_U16_PARTS__; i++)
|
|
{
|
|
uint32_t add = 0; // Variable to hold the sum of the current column
|
|
for (int j = 0; j < __FLOATL2_U16_PARTS__; j++)
|
|
{
|
|
add += temp[j].u16[i]; // Accumulate results from temp
|
|
}
|
|
add += carry; // Add any carry from the previous column
|
|
result.u16[i] = (add & 0xFFFF); // Store the lower 16 bits in result
|
|
carry = ((add >> 16) & 0xFFFF); // Update carry for the next column
|
|
}
|
|
|
|
return result; // Return the final product
|
|
}
|
|
|
|
/**
|
|
* \brief Divides one floatl unsigned number by another.
|
|
*
|
|
* This function performs division of one 128-bit unsigned integer (floatl)
|
|
* by another. It calculates the quotient using a bitwise approach,
|
|
* handling division by zero gracefully. The result is built bit by bit
|
|
* from the most significant bit to the least significant bit. If the
|
|
* divisor is zero, it prints an error message and returns zero.
|
|
*
|
|
* \param[in] a: The dividend (number to be divided).
|
|
* \param[in] b: The divisor (number to divide by).
|
|
* \return The quotient of a divided by b as an floatl (128-bit unsigned integer).
|
|
*/
|
|
static floatl2 floatl2_int_udiv(floatl2 a, floatl2 b, floatl2 *mod)
|
|
{
|
|
int FLOATL2_BIT_PARTS = __FLOATL_BIT_PARTS__ * 2;
|
|
// Check for division by zero
|
|
if (floatl2_int_sign(b) == 0)
|
|
{
|
|
printf("Division by zero!\n");
|
|
return FLOATL2(__FLOATL_INT_ZERO__); /** Handle division by zero */
|
|
}
|
|
|
|
floatl2 result = {0}; // Initialize the result to zero
|
|
floatl2 remainder = {0}; // Initialize the remainder to zero
|
|
|
|
/** Calculate bit by bit from the highest bit */
|
|
for (int i = FLOATL2_BIT_PARTS - 1; i >= 0; i--)
|
|
{
|
|
/** Left shift remainder and add current bit */
|
|
remainder = floatl2_int_shl(remainder, 1); // Shift remainder left by 1
|
|
remainder.u32[0] |= (a.u32[i / 32] >> (i % 32)) & 1; // Add current bit from dividend
|
|
|
|
/** If remainder is greater than or equal to b, subtract b */
|
|
if (floatl2_int_ucmp(remainder, b) >= 0)
|
|
{
|
|
remainder = floatl2_int_sub(remainder, b); // Subtract b from remainder
|
|
result.u32[i / 32] |= (1 << (i % 32)); // Set corresponding bit in result
|
|
}
|
|
}
|
|
|
|
if (mod) *mod = remainder;
|
|
|
|
return result; // Return the final quotient
|
|
}
|
|
|
|
/**
|
|
* \brief Calculate 10 raised to the power of the given integer exponent.
|
|
* \param[in] n: The exponent to which 10 is raised. It can be a positive or negative 32 - bit integer.
|
|
* \return A 'floatl' value representing 10^n.
|
|
*
|
|
* This function calculates 10^n by dividing the exponent into segments of 15.
|
|
* For positive exponents, it multiplies the result by pre - computed powers of 10.
|
|
* For negative exponents, it divides the result by pre - computed powers of 10.
|
|
*/
|
|
static floatl floatl_pow10(int32_t n)
|
|
{
|
|
// Initialize the result to 1 (10^0)
|
|
floatl number = FLOATL_CONST_1;
|
|
|
|
if (n > 0)
|
|
{
|
|
// Calculate 10^n for positive exponents, dividing the exponent into segments of 15
|
|
while (n != 0)
|
|
{
|
|
if (n > 15) {
|
|
// If the exponent is greater than 15, multiply the result by 10^15 and reduce the exponent by 15
|
|
number = floatl_mul(number, flpow10[15]);
|
|
n -= 15;
|
|
}
|
|
else {
|
|
// If the remaining exponent is less than or equal to 15, multiply the result by 10^n
|
|
number = floatl_mul(number, flpow10[n]);
|
|
n = 0;
|
|
}
|
|
}
|
|
}
|
|
else if (n < 0)
|
|
{
|
|
// Calculate 10^n for negative exponents, dividing the exponent into segments of 15
|
|
while (n != 0)
|
|
{
|
|
if (n < -15) {
|
|
// If the absolute value of the exponent is greater than 15, divide the result by 10^15 and increase the exponent by 15
|
|
number = floatl_div(number, flpow10[15]);
|
|
n += 15;
|
|
}
|
|
else {
|
|
// If the absolute value of the remaining exponent is less than or equal to 15, divide the result by 10^(-n)
|
|
number = floatl_div(number, flpow10[-n]);
|
|
n = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
return number;
|
|
}
|
|
|
|
/**
|
|
* \brief Convert a hexadecimal character to its corresponding numerical value.
|
|
* \param[in] c: The hexadecimal character to be converted. It can be a digit from '0' to '9',
|
|
* a lowercase letter from 'a' to 'f', or an uppercase letter from 'A' to 'F'.
|
|
* \return The numerical value corresponding to the hexadecimal character. Returns -1 if the input
|
|
* character is not a valid hexadecimal character.
|
|
*
|
|
* This function takes a single hexadecimal character and converts it to its numerical equivalent.
|
|
* For digits '0' - '9', it returns the digit value. For letters 'a' - 'f' or 'A' - 'F', it returns
|
|
* the corresponding value from 10 to 15.
|
|
*/
|
|
static char hexI(char c)
|
|
{
|
|
// Check if the character is a digit from '0' to '9'
|
|
if (c >= '0' && c <= '9') {
|
|
// If it is a digit, return its numerical value
|
|
return c - '0';
|
|
}
|
|
// Check if the character is a lowercase letter from 'a' to 'f'
|
|
else if (c >= 'a' && c <= 'f') {
|
|
// If it is a lowercase letter, return the corresponding value (10 - 15)
|
|
return c - 'a' + 10;
|
|
}
|
|
// Check if the character is an uppercase letter from 'A' to 'F'
|
|
else if (c >= 'A' && c <= 'F') {
|
|
// If it is an uppercase letter, return the corresponding value (10 - 15)
|
|
return c - 'A' + 10;
|
|
}
|
|
// If the character is not a valid hexadecimal character, return -1
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* \brief Fill the mantissa area of a 'floatl' number with a character value.
|
|
* \param[in,out] number: Pointer to the 'floatl' structure where the mantissa will be updated.
|
|
* \param[in,out] part: Pointer to an integer representing the current u32 part index in the mantissa area.
|
|
* \param[in,out] bitoffset: Pointer to an integer representing the current bit offset within the u32 part.
|
|
* \param[in,out] bitnum: Pointer to an integer representing the number of bits to fill.
|
|
* \param[in] c: The character value to be filled into the mantissa area.
|
|
* \return 1 if the filling operation is successful, 0 if an error occurs (e.g., part index goes out of bounds).
|
|
*
|
|
* This function takes a character value and fills it into the mantissa area of a 'floatl' number.
|
|
* It handles the bit offset and part index to ensure the value is placed correctly in the mantissa.
|
|
*/
|
|
static int pad_mant(floatl *number, int *part, int *bitoffset, int *bitnum, char c)
|
|
{
|
|
// Update the remaining number of bits at the current bit offset
|
|
(*bitoffset) -= (*bitnum);
|
|
|
|
// If the bit offset is negative, we need to move to the previous u32 part
|
|
if ((*bitoffset) < 0)
|
|
{
|
|
(*part)--;
|
|
|
|
// If the u32 part index becomes negative, we need to adjust further
|
|
if ((*part) < 0)
|
|
{
|
|
// Check if the total number of bits to fill is exactly 32. If so, return an error.
|
|
if ((*bitoffset) + (*bitnum) == 32) return 0;
|
|
else
|
|
{
|
|
// Reset the part index to 0
|
|
(*part) = 0;
|
|
// Adjust the number of bits to fill based on the remaining space in the previous part
|
|
(*bitnum) -= (32 - (*bitoffset));
|
|
// Reset the bit offset to 0
|
|
(*bitoffset) = 0;
|
|
}
|
|
}
|
|
|
|
// Update the bit offset within the current part
|
|
(*bitoffset) += 32;
|
|
}
|
|
|
|
// If the current part belongs to the mantissas array
|
|
if ((*part) < __FLOATL_MANT_PARTS__)
|
|
{
|
|
// Concatenate the character 'c' to the mantissas array
|
|
number->mantissas[(*part)] |= (c << (*bitoffset));
|
|
|
|
// If there isn't enough space in the current u32 part to fit the entire character,
|
|
// we need to continue filling the remaining part into the next higher u32 part
|
|
if ((*bitoffset) + (*bitnum) >= 32)
|
|
{
|
|
// If there is a next part in the mantissas array, fill the remaining part there
|
|
if ((*part) + 1 < __FLOATL_MANT_PARTS__) number->mantissas[(*part) + 1] |= (c >> (32 - (*bitoffset)));
|
|
// Otherwise, fill the remaining part into the high - order mantissa
|
|
else number->mantissa |= (c >> (32 - (*bitoffset)));
|
|
}
|
|
}
|
|
// If the current part belongs to the high - order mantissa
|
|
else
|
|
{
|
|
// Concatenate the character 'c' to the high - order mantissa
|
|
number->mantissa |= (c << (*bitoffset));
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* \brief Convert a string representation of a floating - point number to a 'floatl' type.
|
|
* \param[in] str: Pointer to the null - terminated string representing the floating - point number.
|
|
* \return A 'floatl' value representing the converted floating - point number. Returns FLOATL_NAN if the input string is invalid.
|
|
*
|
|
* This function parses a string to create a 'floatl' number. It supports both hexadecimal (starting with "0x")
|
|
* and decimal formats. It handles signs, integer parts, decimal parts, and exponents appropriately.
|
|
*/
|
|
floatl floatl_from(const char *str)
|
|
{
|
|
// Initialize the result number to 0
|
|
floatl number = FLOATL_CONST_0;
|
|
const char *text = str;
|
|
int sign = 1, scale = 0; /* Sign and scale of integer part */
|
|
int e_sign = 1, e_scale = 0; /* Sign and scale of exponent part */
|
|
int p_sign = 1, p_scale = 0; /* Sign and scale of pico part */
|
|
|
|
// Skip leading whitespace characters
|
|
while (*text <= ' ') text++;
|
|
|
|
// Check if the number is negative
|
|
if (*text == '-')
|
|
{
|
|
sign = -1;
|
|
text++;
|
|
}
|
|
|
|
// Check if the number starts with '0'
|
|
if (*text == '0')
|
|
{
|
|
// Skip leading '0's
|
|
while (*text == '0') text++;
|
|
|
|
// Check if it is a hexadecimal number (starts with "0x" or "0X")
|
|
if (*text == 'x' || *text == 'X')
|
|
{
|
|
text++;
|
|
|
|
// Skip leading '0's after "0x"
|
|
while (*text == '0') text++;
|
|
|
|
char c = 0;
|
|
char hide = 0;
|
|
int exp = 0;
|
|
int part = __FLOATL_MANT_PARTS__;
|
|
int bitoffset = __FLOATL_MANT_HIGH_BITS__;
|
|
int bitnum = 0;
|
|
|
|
// Parse the integer part of the hexadecimal number
|
|
while (1)
|
|
{
|
|
c = hexI(*text);
|
|
if (c < 0) break;
|
|
text++;
|
|
|
|
// For the first digit, remove the hidden bit
|
|
if (!hide)
|
|
{
|
|
bitnum = 32 - count_u32_leading_zero(c) - 1;
|
|
c &= ((1 << bitnum) - 1);
|
|
hide = 1;
|
|
}
|
|
else bitnum = 4;
|
|
|
|
if (!pad_mant(&number, &part, &bitoffset, &bitnum, c)) break;
|
|
|
|
exp += bitnum;
|
|
}
|
|
|
|
// Parse the decimal part of the hexadecimal number
|
|
if (*text == '.')
|
|
{
|
|
text++;
|
|
|
|
while (1)
|
|
{
|
|
c = hexI(*text);
|
|
if (c < 0) break;
|
|
text++;
|
|
|
|
// For the first digit, remove the hidden bit
|
|
if (!hide)
|
|
{
|
|
if (c == 0)
|
|
{
|
|
exp -= 4;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
bitnum = 32 - count_u32_leading_zero(c) - 1;
|
|
exp -= (4 - bitnum);
|
|
c &= ((1 << bitnum) - 1);
|
|
hide = 1;
|
|
}
|
|
}
|
|
else bitnum = 4;
|
|
|
|
if (!pad_mant(&number, &part, &bitoffset, &bitnum, c)) break;
|
|
}
|
|
}
|
|
|
|
// Parse the pico part (exponent part in hexadecimal format)
|
|
if (*text == 'p' || *text == 'P')
|
|
{
|
|
text++;
|
|
|
|
// Skip '+' sign
|
|
if (*text == '+') text++;
|
|
// Handle '-' sign
|
|
else if (*text == '-')
|
|
{
|
|
p_sign = -1;
|
|
text++;
|
|
}
|
|
|
|
// Check if the first character of the exponent is a valid digit
|
|
if (!(*text >= '0' && *text <= '9'))
|
|
{
|
|
return FLOATL_NAN;
|
|
}
|
|
|
|
// Convert the exponent part
|
|
while (*text >= '0' && *text <= '9')
|
|
{
|
|
p_scale = (p_scale * 10) + (*text++ - '0'); /* number */
|
|
}
|
|
}
|
|
|
|
// Apply the sign to the pico part
|
|
if (p_sign < 0) p_scale = -p_scale;
|
|
|
|
// Update the exponent
|
|
exp += p_scale;
|
|
|
|
// Check for overflow
|
|
if (exp > __FLOATL_EXP_MID_VALUE__)
|
|
{
|
|
floatl inf = FLOATL_INF;
|
|
inf.sign = sign > 0 ? 0 : 1;
|
|
return inf;
|
|
}
|
|
// Check for underflow
|
|
else if (exp < -__FLOATL_EXP_MID_VALUE__ - 1)
|
|
{
|
|
floatl zoro = FLOATL_CONST_0;
|
|
zoro.sign = sign > 0 ? 0 : 1;
|
|
return zoro;
|
|
}
|
|
|
|
// Set the sign and exponent of the result number
|
|
number.sign = sign > 0 ? 0 : 1;
|
|
number.exponent = __FLOATL_EXP_MID_VALUE__ + exp;
|
|
|
|
return number;
|
|
}
|
|
}
|
|
|
|
// Parse the integer part of the decimal number
|
|
if (*text >= '1' && *text <= '9')
|
|
{
|
|
do
|
|
{
|
|
// Multiply the number by 10 and add the current digit
|
|
number = floatl_mul(number, FLOATL_CONST_10); /* carry addition */
|
|
number = floatl_add(number, floatl_from_d((double)(*text++ - '0')));
|
|
} while (*text >= '0' && *text <= '9');
|
|
}
|
|
|
|
// Parse the decimal part of the decimal number
|
|
if (*text == '.')
|
|
{
|
|
text++;
|
|
|
|
// Check if the first character of the decimal part is a valid digit
|
|
if (!(*text >= '0' && *text <= '9'))
|
|
{
|
|
return FLOATL_NAN;
|
|
}
|
|
|
|
// Multiply the number by 10 and add the current digit, and adjust the scale
|
|
do
|
|
{
|
|
number = floatl_mul(number, FLOATL_CONST_10);
|
|
number = floatl_add(number, floatl_from_d((double)(*text++ - '0')));
|
|
scale--;
|
|
} while (*text >= '0' && *text <= '9');
|
|
}
|
|
|
|
// Parse the exponent part of the decimal number
|
|
if (*text == 'e' || *text == 'E')
|
|
{
|
|
text++;
|
|
|
|
// Skip '+' sign
|
|
if (*text == '+') text++;
|
|
// Handle '-' sign
|
|
else if (*text == '-')
|
|
{
|
|
e_sign = -1;
|
|
text++;
|
|
}
|
|
|
|
// Check if the first character of the exponent is a valid digit
|
|
if (!(*text >= '0' && *text <= '9'))
|
|
{
|
|
return FLOATL_NAN;
|
|
}
|
|
|
|
// Convert the exponent part
|
|
while (*text >= '0' && *text <= '9')
|
|
{
|
|
e_scale = (e_scale * 10) + (*text++ - '0'); /* number */
|
|
}
|
|
}
|
|
|
|
// Calculate the final result
|
|
number = floatl_mul(number, floatl_pow10(scale + e_scale * e_sign));
|
|
number.sign = sign > 0 ? 0 : 1;
|
|
|
|
return number;
|
|
}
|
|
|
|
/**
|
|
* \brief Convert a double - precision floating - point number (double) to a 'floatl' type.
|
|
* \param[in] value: The double - precision floating - point number to be converted.
|
|
* \return A 'floatl' value representing the converted number. Returns FLOATL_NAN if the input is NaN,
|
|
* FLOATL_INF if the input is infinity, and the converted value otherwise.
|
|
*
|
|
* This function takes a double value and converts it to a 'floatl' value. It first checks if the input
|
|
* is NaN or infinity and sets the result accordingly. Otherwise, it extracts the mantissa and exponent
|
|
* from the double and adjusts them to fit the 'floatl' format.
|
|
*/
|
|
floatl floatl_from_d(double value)
|
|
{
|
|
// Initialize the result to 0
|
|
floatl result = {0};
|
|
// Union to access the double value as its components
|
|
double_u f = {.float_ = value};
|
|
|
|
// Check if the input is NaN
|
|
if (f.exponent == 2047 && (f.mantissa_h != 0 || f.mantissa_l != 0))
|
|
{
|
|
result = FLOATL_NAN;
|
|
}
|
|
// Check if the input is infinity
|
|
else if (f.exponent == 2047 && (f.mantissa_h == 0 && f.mantissa_l == 0))
|
|
{
|
|
result = FLOATL_INF;
|
|
}
|
|
else
|
|
{
|
|
// Extract the low and high parts of the mantissa from the double and store them in the 'floatl' mantissa
|
|
result.mantissas[0] = f.mantissa_l;
|
|
result.mantissas[1] = f.mantissa_h;
|
|
// Shift the mantissa to the appropriate position in the 'floatl' format
|
|
result = floatl_int_shl(result, __FLOATL_MANT_BITS__ - 52);
|
|
// Adjust the exponent to fit the 'floatl' format
|
|
result.exponent = (uint32_t)(((int32_t)f.exponent - 1023) + __FLOATL_EXP_MID_VALUE__);
|
|
}
|
|
|
|
// Set the sign of the 'floatl' result
|
|
result.sign = f.sign;
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
/**
|
|
* \brief Converts a custom floating-point type (floatl) to a double precision floating-point number.
|
|
* \param a: The input value of the custom floating-point type (floatl) that needs to be converted.
|
|
* \return A double precision floating-point number which is the converted result of the input floatl value.
|
|
*
|
|
* This function is mainly responsible for converting a value of the custom floating-point type 'floatl'
|
|
* to a standard double precision floating-point number. It first checks if the 'floatl' type uses 64 bits
|
|
* for storage by checking the preprocessor macro 'FLOATL_USE_64BITS'. If it does, it simply copies the memory
|
|
* of the 'floatl' variable to a 'double_u' structure and returns the corresponding floating-point value.
|
|
* Otherwise, it proceeds with a series of operations for the conversion.
|
|
*/
|
|
double floatl_to_d(floatl a)
|
|
{
|
|
double_u du;
|
|
|
|
// If the 'floatl' type uses 64 bits (same as the size of 'double'), directly copy the memory
|
|
// and return the stored value as a 'double'.
|
|
#if defined(FLOATL_USE_64BITS)
|
|
memcpy(&du, &a, sizeof(double));
|
|
return du.float_;
|
|
#endif
|
|
|
|
// Extract the exponent part from the input 'floatl' value. Subtract the middle value of the exponent
|
|
// range defined for 'floatl' (__FLOATL_EXP_MID_VALUE__) to get a relative exponent value.
|
|
int32_t exp = a.exponent - __FLOATL_EXP_MID_VALUE__;
|
|
// Create a new 'floatl' variable'mant' initialized with the input 'a', and then set its sign and exponent
|
|
// to 0 to isolate just the mantissa part of the original 'floatl' value.
|
|
floatl mant = a; mant.sign = 0; mant.exponent = 0U;
|
|
// Set the sign of the result to be the same as the sign of the input 'floatl' value.
|
|
bool sign = a.sign;
|
|
|
|
// Handle special cases such as NaN (Not a Number) and infinity. If the exponent of the input 'floatl'
|
|
// is equal to the whole value of the exponent range defined for 'floatl' (__FLOATL_EXP_WHL_VALUE__),
|
|
// it indicates a special value.
|
|
if (a.exponent == __FLOATL_EXP_WHL_VALUE__)
|
|
{
|
|
// If the mantissa has a sign (checked by 'floatl_int_sign' function), set the high and low parts
|
|
// of the 'int_' structure within 'double_u' to all 1s (representing a special NaN or infinity case).
|
|
if (floatl_int_sign(mant))
|
|
{
|
|
du.int_.high = 0xFFFFFFFF;
|
|
du.int_.low = 0xFFFFFFFF;
|
|
}
|
|
else
|
|
{
|
|
// Otherwise, set the high and low parts to 0.
|
|
du.int_.high = 0;
|
|
du.int_.low = 0;
|
|
}
|
|
|
|
// Set the sign and exponent of the 'double_u' structure according to the rules for special values.
|
|
du.sign = sign;
|
|
du.exponent = 2047;
|
|
|
|
// Return the resulting 'double' value representing the special value.
|
|
return du.float_;
|
|
}
|
|
|
|
// Adjust the extracted exponent to fit within the exponent range of a standard double precision
|
|
// floating-point number (-1022 to 1023). Add 1023 to convert it to the biased exponent form
|
|
// used by doubles.
|
|
exp += 1023;
|
|
|
|
// Check for overflow. If the adjusted exponent is greater than 2047 (the maximum exponent value
|
|
// for a double), it means an overflow has occurred, and we return positive or negative infinity
|
|
// depending on the sign.
|
|
if (exp > 2047)
|
|
{
|
|
du.int_.high = 0;
|
|
du.int_.low = 0;
|
|
du.sign = sign;
|
|
du.exponent = 2047;
|
|
|
|
return du.float_;
|
|
}
|
|
// Check for underflow. If the adjusted exponent is less than 0, it means an underflow has occurred,
|
|
// and we return 0.
|
|
else if (exp < 0)
|
|
{
|
|
du.int_.high = 0;
|
|
du.int_.low = 0;
|
|
du.sign = sign;
|
|
du.exponent = 0;
|
|
|
|
return du.float_;
|
|
}
|
|
|
|
// Add the hidden bit (which is 1 in the normalized representation of floating-point numbers)
|
|
// to the mantissa. Use the 'floatl_int_or' function to perform a bitwise OR operation to set the
|
|
// hidden bit.
|
|
mant = floatl_int_or(mant, __FLOATL_HIDE_MANT_BIT__);
|
|
|
|
// Calculate the number of bits that need to be discarded from the mantissa to fit it into the
|
|
// 52-bit mantissa of a double precision floating-point number. Subtract 52 from the total number
|
|
// of mantissa bits defined for 'floatl' (__FLOATL_MANT_BITS__).
|
|
int32_t discard_bits = __FLOATL_MANT_BITS__ - 52;
|
|
|
|
// Keep only the highest 52 bits of the mantissa. Use the 'floatl_int_shr' function to shift
|
|
// the mantissa right by the number of bits to be discarded.
|
|
floatl double_mant = floatl_int_shr(mant, discard_bits);
|
|
|
|
// Create a 'floatl' variable 'int1' representing the integer value 1. Initialize its first 32-bit
|
|
// part (u32[0]) to 1.
|
|
floatl int1 = __FLOATL_CONST_0__; int1.u32[0] = 1;
|
|
|
|
// Get the highest bit among the bits to be discarded. Use the 'floatl_int_shl' function to shift
|
|
// 'int1' left by the number of bits equal to the number of bits to be discarded minus 1.
|
|
// This bit will be used for rounding purposes.
|
|
floatl high_bit = floatl_int_shl(int1, discard_bits - 1);
|
|
|
|
// Get the bits that will be discarded from the mantissa. Use the 'floatl_int_and' function to perform
|
|
// a bitwise AND operation between the original mantissa and a value that represents the bits to be
|
|
// discarded (formed by first shifting 'int1' left by the number of bits to be discarded and then
|
|
// decrementing it using 'floatl_int_dec').
|
|
floatl round_bits = floatl_int_and(mant, floatl_int_dec(floatl_int_shl(int1, discard_bits)));
|
|
|
|
// Determine whether rounding is needed. If the bits to be discarded ('round_bits') are greater than
|
|
// the highest bit among them ('high_bit'), or if they are equal and the lowest bit of the remaining
|
|
// mantissa ('double_mant') is 1 (checked by 'floatl_int_sign' function), then rounding is required.
|
|
if (floatl_int_cmp(round_bits, high_bit) > 0 ||
|
|
(floatl_int_cmp(round_bits, high_bit) == 0 && floatl_int_sign(floatl_int_and(double_mant, int1))))
|
|
{
|
|
// Perform rounding by incrementing the remaining mantissa. Use the 'floatl_int_inc' function.
|
|
floatl_int_inc(double_mant);
|
|
|
|
// Check if the increment of the mantissa causes a carry to the exponent part. If the sign bit
|
|
// of the result of a bitwise AND operation between the remaining mantissa and a value representing
|
|
// the bit position 2 bits higher than the hidden bit position (shifted 'int1' left by 52 + 2)
|
|
// is 1, it means there is a carry to the exponent.
|
|
if (floatl_int_sign(floatl_int_and(double_mant, floatl_int_shl(int1, 52 + 2))))
|
|
{
|
|
// If there is a carry to the exponent, shift the remaining mantissa right by 1 bit
|
|
// to make room for the carry in the exponent. Use the 'floatl_int_shr' function.
|
|
double_mant = floatl_int_shr(double_mant, 1);
|
|
// Increment the exponent by 1.
|
|
exp += 1;
|
|
|
|
// Check for a secondary overflow of the exponent. If the exponent after the increment is
|
|
// greater than 2047, it means another overflow has occurred, and we return positive or
|
|
// negative infinity depending on the sign.
|
|
if (exp > 2047)
|
|
{
|
|
du.int_.high = 0;
|
|
du.int_.low = 0;
|
|
du.sign = sign;
|
|
du.exponent = 2047;
|
|
return du.float_;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Combine the processed mantissa, sign, and exponent to form the final 'double' value.
|
|
// Set the high and low parts of the 'int_' structure within 'double_u' to the corresponding parts
|
|
// of the remaining mantissa.
|
|
du.int_.high = double_mant.u32[1];
|
|
du.int_.low = double_mant.u32[0];
|
|
// Set the sign of the 'double_u' structure.
|
|
du.sign = sign;
|
|
// Set the exponent of the 'double_u' structure.
|
|
du.exponent = exp;
|
|
|
|
// Return the final converted double precision floating-point number.
|
|
return du.float_;
|
|
}
|
|
|
|
/**
|
|
* \brief Get the sign of a 'floatl' number.
|
|
* \param[in] a: The 'floatl' number whose sign is to be determined.
|
|
* \return The sign of the 'floatl' number. It returns the value of the 'sign' field in the 'floatl' structure.
|
|
*
|
|
* This function simply extracts and returns the sign bit of the given 'floatl' number.
|
|
*/
|
|
int floatl_sign(floatl a)
|
|
{
|
|
return a.sign;
|
|
}
|
|
|
|
/**
|
|
* \brief Check if a 'floatl' number is NaN (Not a Number).
|
|
* \param[in] a: The 'floatl' number to be checked.
|
|
* \return 1 if the 'floatl' number is NaN, 0 otherwise.
|
|
*
|
|
* This function checks if the 'floatl' number is NaN by performing a bitwise AND operation
|
|
* between the number and a mask (__FLOATL_ALL_EXP_MANT__) and then comparing the result
|
|
* with the representation of infinity (__FLOATL_INF__). If the result is greater than the
|
|
* infinity representation, the number is considered NaN.
|
|
*/
|
|
int floatl_isnan(floatl a)
|
|
{
|
|
return floatl_int_ucmp(floatl_int_and(a, __FLOATL_ALL_EXP_MANT__), __FLOATL_INF__) > 0;
|
|
}
|
|
|
|
/**
|
|
* \brief Check if a 'floatl' number is infinity.
|
|
* \param[in] a: The 'floatl' number to be checked.
|
|
* \return 1 if the 'floatl' number is infinity, 0 otherwise.
|
|
*
|
|
* This function checks if the 'floatl' number is infinity by performing a bitwise AND operation
|
|
* between the number and a mask (__FLOATL_ALL_EXP_MANT__) and then comparing the result
|
|
* with the representation of infinity (__FLOATL_INF__). If the result is equal to the
|
|
* infinity representation, the number is considered infinity.
|
|
*/
|
|
int floatl_isinf(floatl a)
|
|
{
|
|
return floatl_int_ucmp(floatl_int_and(a, __FLOATL_ALL_EXP_MANT__), __FLOATL_INF__) == 0;
|
|
}
|
|
|
|
/**
|
|
* \brief Check if a 'floatl' number is a normal number.
|
|
* \param[in] a: The 'floatl' number to be checked.
|
|
* \return 1 if the 'floatl' number is a normal number, 0 otherwise.
|
|
*
|
|
* A normal number is defined as a number whose exponent is neither 0 nor the maximum possible
|
|
* exponent value (__FLOATL_EXP_WHL_VALUE__). This function checks these conditions and
|
|
* returns the appropriate result.
|
|
*/
|
|
int floatl_isnormal(floatl a)
|
|
{
|
|
return ((a.exponent != 0) && (a.exponent != __FLOATL_EXP_WHL_VALUE__));
|
|
}
|
|
|
|
/**
|
|
* \brief Compare if a 'floatl' number 'a' is less than another 'floatl' number 'b'.
|
|
* \param[in] a: The first 'floatl' number to be compared.
|
|
* \param[in] b: The second 'floatl' number to be compared.
|
|
* \return true if 'a' is less than 'b', false otherwise.
|
|
*
|
|
* This function compares two 'floatl' numbers to determine if 'a' is less than 'b'.
|
|
* It first checks the signs of the two numbers. If the signs are different,
|
|
* it checks if 'a' is negative and the result of a bit - shifted OR operation of 'a' and 'b' is non - zero.
|
|
* If the signs are the same, it checks if the two numbers are not equal and applies a condition based on the sign.
|
|
*/
|
|
static bool floatl_real_lt(floatl a, floatl b)
|
|
{
|
|
// Extract the sign of 'a'
|
|
bool sign1 = a.sign;
|
|
// Extract the sign of 'b'
|
|
bool sign2 = b.sign;
|
|
// Check if the signs of the two numbers are different
|
|
if (sign1 != sign2) // This can also be checked using XOR
|
|
{
|
|
// If 'a' is negative and the result of a bit - shifted OR operation of 'a' and 'b' is non - zero,
|
|
// then 'a' is less than 'b'
|
|
return sign1 && floatl_int_sign(floatl_int_shl(floatl_int_or(a, b), 1));
|
|
}
|
|
// If the signs are the same, 'a' is less than 'b' if the two numbers are not equal
|
|
// and for positive numbers, the one with a smaller absolute value is smaller,
|
|
// while for negative numbers, the one with a larger absolute value is smaller
|
|
return (floatl_int_ucmp(a, b)) && (sign1 ^ (floatl_int_ucmp(a, b) < 0));
|
|
}
|
|
|
|
/**
|
|
* \brief Compare if a 'floatl' number 'a' is less than or equal to another 'floatl' number 'b'.
|
|
* \param[in] a: The first 'floatl' number to be compared.
|
|
* \param[in] b: The second 'floatl' number to be compared.
|
|
* \return true if 'a' is less than or equal to 'b', false otherwise.
|
|
*
|
|
* This function compares two 'floatl' numbers to determine if 'a' is less than or equal to 'b'.
|
|
* It first checks the signs of the two numbers. If the signs are different,
|
|
* it checks if 'a' is negative or the result of a bit - shifted OR operation of 'a' and 'b' is zero.
|
|
* If the signs are the same, it checks if the two numbers are equal or the appropriate condition based on the sign holds.
|
|
*/
|
|
static bool floatl_real_le(floatl a, floatl b)
|
|
{
|
|
// Extract the sign of 'a'
|
|
bool sign1 = a.sign;
|
|
// Extract the sign of 'b'
|
|
bool sign2 = b.sign;
|
|
// Check if the signs of the two numbers are different
|
|
if (sign1 != sign2)
|
|
{
|
|
// Different from the 'lt' function, the numbers can be zero here.
|
|
// If 'a' is negative or the result of a bit - shifted OR operation of 'a' and 'b' is zero,
|
|
// then 'a' is less than or equal to 'b'
|
|
return sign1 || (floatl_int_sign(floatl_int_shl(floatl_int_or(a, b), 1)) == 0);
|
|
}
|
|
// Different from the 'lt' function, we check for equality or less - than condition.
|
|
// 'a' is less than or equal to 'b' if the two numbers are equal or the appropriate condition based on the sign holds.
|
|
return (!floatl_int_ucmp(a, b)) || (sign1 ^ (floatl_int_ucmp(a, b) < 0));
|
|
}
|
|
|
|
/**
|
|
* \brief Check if two 'floatl' numbers are equal.
|
|
* \param[in] a: The first 'floatl' number to be compared.
|
|
* \param[in] b: The second 'floatl' number to be compared.
|
|
* \return 1 if the two numbers are equal, 0 otherwise.
|
|
*
|
|
* This function first checks if either of the numbers is a NaN (Not a Number).
|
|
* If so, it immediately returns 0 because NaN is not equal to any number, including itself.
|
|
* Otherwise, it checks if the two numbers are equal by comparing their bit - representations
|
|
* using floatl_int_ucmp. It also considers the case where both numbers might be zero
|
|
* by using floatl_int_sign and floatl_int_shl along with floatl_int_or.
|
|
*/
|
|
int floatl_eq(floatl a, floatl b)
|
|
{
|
|
// If either number is a NaN, they are not equal
|
|
if (floatl_isnan(a) || floatl_isnan(b))
|
|
return 0;
|
|
return
|
|
// Check if the bit - representations of the two numbers are equal
|
|
(!floatl_int_ucmp(a, b)) ||
|
|
// Check if both numbers are effectively zero
|
|
((floatl_int_sign(floatl_int_shl(floatl_int_or(a, b), 1)) == 0));
|
|
}
|
|
|
|
/**
|
|
* \brief Check if two 'floatl' numbers are not equal.
|
|
* \param[in] a: The first 'floatl' number to be compared.
|
|
* \param[in] b: The second 'floatl' number to be compared.
|
|
* \return 1 if the two numbers are not equal, 0 otherwise.
|
|
*
|
|
* This function simply negates the result of the floatl_eq function.
|
|
* If floatl_eq returns 1 (equal), this function returns 0 (not equal), and vice versa.
|
|
*/
|
|
int floatl_ne(floatl a, floatl b)
|
|
{
|
|
return !floatl_eq(a, b);
|
|
}
|
|
|
|
/**
|
|
* \brief Check if the first 'floatl' number is less than the second.
|
|
* \param[in] a: The first 'floatl' number to be compared.
|
|
* \param[in] b: The second 'floatl' number to be compared.
|
|
* \return 1 if a < b, 0 otherwise.
|
|
*
|
|
* This function first checks if either of the numbers is a NaN.
|
|
* If so, it returns 0 because NaN cannot be compared in a meaningful way.
|
|
* Otherwise, it calls the floatl_real_lt function to perform the actual less - than comparison.
|
|
*/
|
|
int floatl_lt(floatl a, floatl b)
|
|
{
|
|
if (floatl_isnan(a) || floatl_isnan(b))
|
|
return 0;
|
|
return floatl_real_lt(a, b);
|
|
}
|
|
|
|
/**
|
|
* \brief Check if the first 'floatl' number is less than or equal to the second.
|
|
* \param[in] a: The first 'floatl' number to be compared.
|
|
* \param[in] b: The second 'floatl' number to be compared.
|
|
* \return 1 if a <= b, 0 otherwise.
|
|
*
|
|
* This function first checks if either of the numbers is a NaN.
|
|
* If so, it returns 0. Otherwise, it calls the floatl_real_le function
|
|
* to perform the actual less - than or equal comparison.
|
|
*/
|
|
int floatl_le(floatl a, floatl b)
|
|
{
|
|
if (floatl_isnan(a) || floatl_isnan(b))
|
|
return 0;
|
|
return floatl_real_le(a, b);
|
|
}
|
|
|
|
/**
|
|
* \brief Check if the first 'floatl' number is greater than the second.
|
|
* \param[in] a: The first 'floatl' number to be compared.
|
|
* \param[in] b: The second 'floatl' number to be compared.
|
|
* \return 1 if a > b, 0 otherwise.
|
|
*
|
|
* This function first checks if either of the numbers is a NaN.
|
|
* If so, it returns 0. Otherwise, it negates the result of the floatl_real_le function
|
|
* because a > b is equivalent to!(a <= b).
|
|
*/
|
|
int floatl_gt(floatl a, floatl b)
|
|
{
|
|
if (floatl_isnan(a) || floatl_isnan(b))
|
|
return 0;
|
|
return !floatl_real_le(a, b);
|
|
}
|
|
|
|
/**
|
|
* \brief Check if the first 'floatl' number is greater than or equal to the second.
|
|
* \param[in] a: The first 'floatl' number to be compared.
|
|
* \param[in] b: The second 'floatl' number to be compared.
|
|
* \return 1 if a >= b, 0 otherwise.
|
|
*
|
|
* This function first checks if either of the numbers is a NaN.
|
|
* If so, it returns 0. Otherwise, it negates the result of the floatl_real_lt function
|
|
* because a >= b is equivalent to!(a < b).
|
|
*/
|
|
int floatl_ge(floatl a, floatl b)
|
|
{
|
|
if (floatl_isnan(a) || floatl_isnan(b))
|
|
return 0;
|
|
return !floatl_real_lt(a, b);
|
|
}
|
|
|
|
/**
|
|
* \brief Compute the absolute value of a 'floatl' number.
|
|
* \param[in] a: The 'floatl' number for which the absolute value is to be computed.
|
|
* \return A 'floatl' number representing the absolute value of the input number.
|
|
*
|
|
* This function takes a 'floatl' number and checks its sign bit. If the number is negative
|
|
* (i.e., the sign bit is set), it clears the sign bit to make the number positive.
|
|
* If the number is already positive, it remains unchanged.
|
|
*/
|
|
floatl floatl_abs(floatl a)
|
|
{
|
|
// Check if the number is negative
|
|
if (a.sign)
|
|
// If negative, set the sign bit to 0 (make it positive)
|
|
a.sign = 0;
|
|
return a;
|
|
}
|
|
|
|
/**
|
|
* \brief Compute the negation of a 'floatl' number.
|
|
* \param[in] a: The 'floatl' number to be negated.
|
|
* \return A 'floatl' number representing the negation of the input number.
|
|
*
|
|
* This function takes a 'floatl' number and toggles its sign bit. If the number is positive,
|
|
* it becomes negative; if the number is negative, it becomes positive.
|
|
*/
|
|
floatl floatl_neg(floatl a)
|
|
{
|
|
// Toggle the sign bit of the number
|
|
a.sign = !a.sign;
|
|
return a;
|
|
}
|
|
|
|
/**
|
|
* \brief Computes the ceiling of a 'floatl' number.
|
|
* \param[in] a: The 'floatl' number for which the ceiling value is to be computed.
|
|
* \return A 'floatl' number representing the ceiling of the input number.
|
|
*
|
|
* This function calculates the ceiling of a 'floatl' number. The ceiling of a number is the smallest
|
|
* integer that is greater than or equal to the given number.
|
|
*/
|
|
floatl floatl_ceil(floatl a)
|
|
{
|
|
// Calculate the actual exponent value by subtracting the mid - point of the exponent range
|
|
int32_t exponent = a.exponent - __FLOATL_EXP_MID_VALUE__;
|
|
|
|
// If the exponent is negative, the number lies between - 1 and 1
|
|
if (exponent < 0)
|
|
{
|
|
// If the number is greater than 0, the ceiling is 1
|
|
if (floatl_gt(a, FLOATL_CONST_0))
|
|
{
|
|
return FLOATL_CONST_1;
|
|
}
|
|
// If the number is equal to 0, the ceiling is 0
|
|
else if (floatl_eq(a, FLOATL_CONST_0))
|
|
{
|
|
return FLOATL_CONST_0;
|
|
}
|
|
// If the number is less than 0, the ceiling is 0 (in the context of floating - point representation)
|
|
else
|
|
{
|
|
return floatl_neg(FLOATL_CONST_0);
|
|
}
|
|
}
|
|
|
|
// Calculate the number of bits to shift to isolate the integer part
|
|
int32_t shift = __FLOATL_MANT_BITS__ - exponent;
|
|
|
|
// If the shift is less than or equal to 0, the number is already an integer
|
|
if (shift <= 0) return a;
|
|
|
|
// Create a floatl number with the least significant bit set to 1
|
|
floatl int1 = FLOATL_CONST_0;
|
|
int1.u32[0] = 1;
|
|
// Create a mask to isolate the fractional part
|
|
floatl mask = floatl_int_sub(floatl_int_shl(int1, shift), int1);
|
|
// Clear the fractional part of the number
|
|
floatl result = floatl_int_and(a, floatl_int_not(mask));
|
|
|
|
// If the result is less than the original number, increment the result
|
|
if (floatl_lt(result, a))
|
|
{
|
|
result = floatl_add(result, FLOATL_CONST_1);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* \brief Computes the floor of a 'floatl' number.
|
|
* \param[in] a: The 'floatl' number for which the floor value is to be computed.
|
|
* \return A 'floatl' number representing the floor of the input number.
|
|
*
|
|
* This function calculates the floor of a 'floatl' number. The floor of a number is the largest
|
|
* integer that is less than or equal to the given number.
|
|
*/
|
|
floatl floatl_floor(floatl a)
|
|
{
|
|
// Calculate the actual exponent value by subtracting the mid - point of the exponent range
|
|
int32_t exponent = a.exponent - __FLOATL_EXP_MID_VALUE__;
|
|
|
|
// If the exponent is negative, the number lies between - 1 and 1
|
|
if (exponent < 0)
|
|
{
|
|
// If the number is greater than or equal to 0, the floor is 0
|
|
if (floatl_ge(a, FLOATL_CONST_0))
|
|
{
|
|
return FLOATL_CONST_0;
|
|
}
|
|
// If the number is less than 0, the floor is - 1
|
|
else
|
|
{
|
|
return floatl_neg(FLOATL_CONST_1);
|
|
}
|
|
}
|
|
|
|
// Calculate the number of bits to shift to isolate the integer part
|
|
int32_t shift = __FLOATL_MANT_BITS__ - exponent;
|
|
|
|
// If the shift is less than or equal to 0, the number is already an integer
|
|
if (shift <= 0) return a;
|
|
|
|
// Create a floatl number with the least significant bit set to 1
|
|
floatl int1 = FLOATL_CONST_0;
|
|
int1.u32[0] = 1;
|
|
// Create a mask to isolate the fractional part
|
|
floatl mask = floatl_int_sub(floatl_int_shl(int1, shift), int1);
|
|
// Clear the fractional part of the number
|
|
floatl result = floatl_int_and(a, floatl_int_not(mask));
|
|
|
|
// If the result is greater than the original number, decrement the result
|
|
if (floatl_gt(result, a))
|
|
{
|
|
result = floatl_sub(result, FLOATL_CONST_1);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* \brief Rounds a 'floatl' number to the nearest integer.
|
|
* \param[in] a: The 'floatl' number to be rounded.
|
|
* \return A 'floatl' number representing the rounded value of the input number.
|
|
*
|
|
* This function rounds a 'floatl' number to the nearest integer. If the fractional part
|
|
* is 0.5 or greater, the number is rounded up; otherwise, it is rounded down.
|
|
*/
|
|
floatl floatl_round(floatl a)
|
|
{
|
|
// Calculate the actual exponent value by subtracting the mid - point of the exponent range
|
|
int32_t exponent = a.exponent - __FLOATL_EXP_MID_VALUE__;
|
|
|
|
// If the exponent is negative, the number lies between - 1 and 1
|
|
if (exponent < 0)
|
|
{
|
|
if (floatl_gt(a, FLOATL_CONST_0))
|
|
{
|
|
// If the number is greater than 0, round up if it is greater than or equal to 0.5
|
|
return (floatl_ge(a, floatl(0.5))) ? FLOATL_CONST_1 : FLOATL_CONST_0;
|
|
}
|
|
else
|
|
{
|
|
// If the number is less than 0, round down if it is less than or equal to - 0.5
|
|
return (floatl_le(a, floatl(-0.5))) ? floatl_neg(FLOATL_CONST_1) : floatl_neg(FLOATL_CONST_0);
|
|
}
|
|
}
|
|
|
|
// Calculate the number of bits to shift to isolate the integer part
|
|
int32_t shift = __FLOATL_MANT_BITS__ - exponent;
|
|
|
|
// If the shift is less than or equal to 0, the number is already an integer
|
|
if (shift <= 0) return a;
|
|
|
|
// Create a floatl number with the least significant bit set to 1
|
|
floatl int1 = FLOATL_CONST_0;
|
|
int1.u32[0] = 1;
|
|
// Create a mask to isolate the fractional part
|
|
floatl mask = floatl_int_sub(floatl_int_shl(int1, shift), int1);
|
|
// Clear the fractional part of the number to get the integer part
|
|
floatl result = floatl_int_and(a, floatl_int_not(mask));
|
|
// Extract the fractional part of the number
|
|
floatl fractional = floatl_int_and(a, mask);
|
|
|
|
// Check if the fractional part is greater than or equal to 0.5
|
|
if (floatl_int_ucmp(fractional, floatl_int_shl(int1, shift - 1)) >= 0)
|
|
{
|
|
// If so, round up the result
|
|
result = floatl_add(result, FLOATL_CONST_1);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* \brief Perform a right shift operation on a 'floatl' value while preserving the sticky bit.
|
|
* \param[in] mant: The 'floatl' value to be shifted.
|
|
* \param[in] n: The number of bits to shift to the right.
|
|
* \return A 'floatl' value representing the result of the right shift operation with the sticky bit preserved.
|
|
*
|
|
* This function performs a right shift operation on the given 'floatl' value. If the number of bits to shift
|
|
* is less than the total number of bits in a 'floatl' minus 1, it uses the floatl_int_shr function to perform
|
|
* the shift. Then, it checks if there are any non - zero bits that would be shifted out by using floatl_int_sign
|
|
* and floatl_int_shl. If so, it sets the least significant bit of the result to 1. If the number of bits to shift
|
|
* is greater than or equal to the total number of bits in a 'floatl' minus 1, it checks if the original value
|
|
* is non - zero and sets the least significant bit of the result accordingly.
|
|
*/
|
|
static floatl shift_right_sticky(floatl mant, uint32_t n)
|
|
{
|
|
// Initialize the result to zero
|
|
floatl res = __FLOATL_INT_ZERO__;
|
|
if (n < __FLOATL_BIT_PARTS__ - 1) {
|
|
// Perform the right shift operation
|
|
res = floatl_int_shr(mant, n);
|
|
// Check if there are non - zero bits shifted out
|
|
if (floatl_int_sign(floatl_int_shl(mant, __FLOATL_BIT_PARTS__ - n)))
|
|
{
|
|
// Set the least significant bit of the result
|
|
res.u32[0] |= 1;
|
|
}
|
|
}
|
|
else {
|
|
// Check if the original value is non - zero
|
|
if (floatl_int_sign(mant))
|
|
{
|
|
// Set the least significant bit of the result
|
|
res.u32[0] |= 1;
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* \brief Perform a right shift operation on the product of two 'floatl' values while preserving the sticky bit.
|
|
* \param[in] mant1: The first 'floatl' value.
|
|
* \param[in] mant2: The second 'floatl' value.
|
|
* \return A 'floatl' value representing the result of the right shift operation on the product of the two 'floatl' values
|
|
* with the sticky bit preserved.
|
|
*
|
|
* This function first multiplies the two given 'floatl' values using floatl2_int_umul and stores the result in a 'floatl2'
|
|
* structure. Then, it performs a right shift operation on the product using floatl2_int_shr. It checks if there are any
|
|
* non - zero bits that would be shifted out by using floatl2_int_sign and floatl2_int_shl. If so, it sets the least
|
|
* significant bit of the result. Finally, it returns the lower part of the 'floatl2' result as a 'floatl' value.
|
|
*/
|
|
static floatl shift_right_sticky_fl2(floatl mant1, floatl mant2)
|
|
{
|
|
// Initialize the result and temporary 'floatl2' structures to zero
|
|
floatl2 res = {0};
|
|
floatl2 temp = {0};
|
|
|
|
// Multiply the two 'floatl' values
|
|
temp = floatl2_int_umul(FLOATL2(mant1), FLOATL2(mant2));
|
|
// Perform the right shift operation on the product
|
|
res = floatl2_int_shr(temp, __FLOATL_MANT_BITS__ - 2);
|
|
// Check if there are non - zero bits shifted out
|
|
if (floatl2_int_sign(floatl2_int_shl(temp, __FLOATL_BIT_PARTS__ * 2 - (__FLOATL_MANT_BITS__ - 2))))
|
|
{
|
|
// Set the least significant bit of the result
|
|
res.u32[0] |= 1;
|
|
}
|
|
|
|
// Return the lower part of the 'floatl2' result
|
|
return res.low;
|
|
}
|
|
|
|
/**
|
|
* \brief Perform a round - to - even operation on a 'floatl' value using the GRS (Guard, Round, Sticky) bits.
|
|
* \param[in] mant: The 'floatl' value to be rounded.
|
|
* \return A 'floatl' value representing the result of the round - to - even operation.
|
|
*
|
|
* This function extracts the lower 3 bits (GRS bits) of the given 'floatl' value. Then, it shifts the 'floatl' value
|
|
* to the right by 3 bits. It creates a 'floatl' value with the least significant bit set to 1. Based on the value of
|
|
* the GRS bits and the least significant bit of the shifted value, it decides whether to increment the shifted value.
|
|
* If the GRS bits are greater than 4 or equal to 4 and the least significant bit of the shifted value is 1, it increments
|
|
* the shifted value using floatl_int_inc. Finally, it returns the rounded 'floatl' value.
|
|
*/
|
|
static floatl round_even_grs(floatl mant)
|
|
{
|
|
// Extract the GRS (Guard, Round, Sticky) bits
|
|
uint8_t grs = mant.u32[0] & 7;
|
|
|
|
// Shift the 'floatl' value to the right by 3 bits
|
|
mant = floatl_int_shr(mant, 3);
|
|
|
|
// Create a 'floatl' value with the least significant bit set to 1
|
|
floatl u1 = __FLOATL_INT_ZERO__;
|
|
u1.u32[0] = 1U;
|
|
// Check the conditions for rounding
|
|
if ((grs > 4) || ((grs == 4) && (mant.u32[0] & 1)))
|
|
// Increment the shifted value if the conditions are met
|
|
mant = floatl_int_inc(mant);
|
|
|
|
return mant;
|
|
}
|
|
|
|
/**
|
|
* \brief Determine whether the result should be NaN or Inf based on the sign and mantissa.
|
|
* \param[in] sign: A boolean value representing the sign. If true, it indicates a negative sign; otherwise, a positive sign.
|
|
* \param[in] mant: A 'floatl' type mantissa value used to determine if the result should be NaN.
|
|
* \return A 'floatl' value representing either NaN or Inf with the appropriate sign.
|
|
*
|
|
* This function first initializes the result as infinity. Then, it checks the mantissa. If the mantissa is non - zero,
|
|
* it returns NaN. Otherwise, it sets the sign bit of the infinity value and returns the result.
|
|
*/
|
|
static inline floatl floatl_nan_inf(bool sign, floatl mant)
|
|
{
|
|
// Initialize the result as infinity
|
|
floatl result = __FLOATL_INF__;
|
|
// Check if the mantissa is non - zero. If so, return NaN
|
|
if (floatl_int_sign(mant))
|
|
return __FLOATL_NAN__;
|
|
// Set the sign bit of the infinity value
|
|
result.u32[__FLOATL_U32_PARTS__ - 1] |= ((uint32_t)sign << 31);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* \brief Count the number of leading zero bits in a 32 - bit unsigned integer.
|
|
* \param[in] x: A 32 - bit unsigned integer whose leading zero bits are to be counted.
|
|
* \return The number of leading zero bits in the input 32 - bit unsigned integer.
|
|
*
|
|
* This function uses a binary search - like approach to count the leading zero bits efficiently.
|
|
* If the input is zero, it immediately returns 32. Otherwise, it narrows down the search range
|
|
* by shifting the number and incrementing a counter accordingly.
|
|
*/
|
|
static int count_u32_leading_zero(uint32_t x)
|
|
{
|
|
// If the input is zero, there are 32 leading zero bits
|
|
if (x == 0)
|
|
return 32;
|
|
int n = 1;
|
|
// Check the higher 16 bits. If they are zero, increment the counter and shift the number
|
|
if ((x >> 16) == 0) { n += 16; x <<= 16; }
|
|
// Check the next 8 bits. If they are zero, increment the counter and shift the number
|
|
if ((x >> 24) == 0) { n += 8; x <<= 8; }
|
|
// Check the next 4 bits. If they are zero, increment the counter and shift the number
|
|
if ((x >> 28) == 0) { n += 4; x <<= 4; }
|
|
// Check the next 2 bits. If they are zero, increment the counter and shift the number
|
|
if ((x >> 30) == 0) { n += 2; x <<= 2; }
|
|
// Adjust the counter based on the most significant bit
|
|
n -= (x >> 31);
|
|
return n;
|
|
}
|
|
|
|
/**
|
|
* \brief Count the total number of leading zero bits in a 'floatl' type value.
|
|
* \param[in] x: A 'floatl' type value whose leading zero bits are to be counted.
|
|
* \return The total number of leading zero bits in the input 'floatl' value.
|
|
*
|
|
* This function iterates over the 32 - bit parts of the 'floatl' value from the most significant part to the least significant part.
|
|
* If a part is zero, it adds 32 to the counter. If a non - zero part is found, it counts the leading zero bits in that part
|
|
* using the count_u32_leading_zero function and then stops the iteration.
|
|
*/
|
|
static int count_leading_zero(floatl x)
|
|
{
|
|
int n = 0;
|
|
|
|
// Iterate over the 32 - bit parts of the 'floatl' value from the most significant part to the least significant part
|
|
for (int i = __FLOATL_U32_PARTS__ - 1; i >= 0; i--)
|
|
{
|
|
if (x.u32[i] == 0)
|
|
{
|
|
// If the current part is zero, add 32 to the counter
|
|
n += 32;
|
|
}
|
|
else
|
|
{
|
|
// If the current part is non - zero, count the leading zero bits in this part and break the loop
|
|
n += count_u32_leading_zero(x.u32[i]);
|
|
break;
|
|
}
|
|
}
|
|
return n;
|
|
}
|
|
|
|
/**
|
|
* \brief Perform addition of two 'floatl' numbers by adding their absolute values.
|
|
* The sign of the result is determined by the sign of the first parameter.
|
|
* \param[in] a: The first 'floatl' number.
|
|
* \param[in] b: The second 'floatl' number.
|
|
* \return A 'floatl' number representing the result of the addition.
|
|
*
|
|
* This function adds the absolute values of two 'floatl' numbers. It first extracts
|
|
* the exponents and mantissas of the input numbers. Then it handles special cases
|
|
* such as when the exponents are the same, or when one of the numbers is NaN or Inf.
|
|
* After that, it aligns the mantissas according to the exponent difference, adds them,
|
|
* and performs rounding and normalization steps. Finally, it checks for overflow and
|
|
* returns the result with the appropriate sign.
|
|
*/
|
|
static floatl real_floatl_add(floatl a, floatl b)
|
|
{
|
|
// Extract the exponent of the first number
|
|
uint32_t exp1 = a.exponent;
|
|
// Extract the exponent of the second number
|
|
uint32_t exp2 = b.exponent;
|
|
// Extract the mantissa of the first number, setting sign and exponent to 0
|
|
floatl mant1 = a; mant1.sign = 0; mant1.exponent = 0U;
|
|
// Extract the mantissa of the second number, setting sign and exponent to 0
|
|
floatl mant2 = b; mant2.sign = 0; mant2.exponent = 0U;
|
|
// Set the sign of the result to the sign of the first number
|
|
bool sign = a.sign;
|
|
// Calculate the difference between the exponents
|
|
int32_t expdiff = exp1 - exp2;
|
|
|
|
// Check if the exponents are the same
|
|
if (expdiff == 0)
|
|
{
|
|
// Handle denormalized numbers
|
|
if (exp1 == 0)
|
|
{
|
|
a = __FLOATL_INT_ZERO__;
|
|
a.sign = sign;
|
|
return a;
|
|
}
|
|
|
|
// Check for infinity or NaN
|
|
if (exp1 == __FLOATL_ALL_EXP__.exponent)
|
|
{
|
|
return floatl_nan_inf(sign, floatl_int_or(mant1, mant2));
|
|
}
|
|
}
|
|
|
|
// Check if the first number is NaN or Inf
|
|
if (exp1 == __FLOATL_ALL_EXP__.exponent) return floatl_nan_inf(sign, mant1);
|
|
// Check if the second number is NaN or Inf
|
|
if (exp2 == __FLOATL_ALL_EXP__.exponent) return floatl_nan_inf(sign, mant2);
|
|
|
|
// If the first number is denormalized, return the second number with the appropriate sign
|
|
if (exp1 == 0)
|
|
{
|
|
b.sign = sign;
|
|
return b;
|
|
}
|
|
|
|
// If the second number is denormalized, return the first number with the appropriate sign
|
|
if (exp2 == 0)
|
|
{
|
|
a.sign = sign;
|
|
return a;
|
|
}
|
|
|
|
// Add the hidden bit (1) to the mantissas
|
|
mant1 = floatl_int_or(mant1, __FLOATL_HIDE_MANT_BIT__);
|
|
mant2 = floatl_int_or(mant2, __FLOATL_HIDE_MANT_BIT__);
|
|
|
|
// Shift the mantissas left by 2 bits
|
|
mant1 = floatl_int_shl(mant1, 2);
|
|
mant2 = floatl_int_shl(mant2, 2);
|
|
|
|
// If the exponent of the first number is smaller
|
|
if (expdiff < 0)
|
|
{
|
|
expdiff = -expdiff;
|
|
// Shift the first mantissa right by the exponent difference, preserving the sticky bit
|
|
mant1 = shift_right_sticky(mant1, expdiff);
|
|
exp1 = exp2;
|
|
}
|
|
// If the exponent of the first number is larger
|
|
else if (expdiff > 0)
|
|
{
|
|
// Shift the second mantissa right by the exponent difference, preserving the sticky bit
|
|
mant2 = shift_right_sticky(mant2, expdiff);
|
|
}
|
|
|
|
// Add the two mantissas
|
|
floatl mant = floatl_int_add(mant1, mant2);
|
|
|
|
// Check if the result mantissa is less than the maximum allowed value
|
|
if (floatl_int_ucmp(mant, __FLOATL_MANT_PLUS_MAX__) < 0)
|
|
{
|
|
// Shift the mantissa left by 1 bit
|
|
mant = floatl_int_shl(mant, 1);
|
|
}
|
|
else
|
|
{
|
|
// Increment the exponent if the mantissa overflows
|
|
exp1++;
|
|
}
|
|
|
|
// Perform round - to - even rounding on the mantissa
|
|
mant = round_even_grs(mant);
|
|
// Check if the rounded mantissa exceeds the maximum value
|
|
if (floatl_int_ucmp(mant, __FLOATL_MANT_WHL__) > 0)
|
|
{
|
|
// Increment the exponent if the rounded mantissa overflows
|
|
exp1++;
|
|
}
|
|
|
|
// Check for overflow of the exponent
|
|
if (exp1 > ((uint32_t)(__FLOATL_ALL_EXP__.exponent) - 1))
|
|
{
|
|
a = __FLOATL_INF__;
|
|
a.sign = sign;
|
|
return a;
|
|
}
|
|
|
|
// Set the result number with the new mantissa, sign, and exponent
|
|
a = mant;
|
|
a.sign = sign;
|
|
a.exponent = exp1;
|
|
|
|
return a;
|
|
}
|
|
|
|
/**
|
|
* \brief Perform subtraction of two 'floatl' numbers by subtracting their absolute values.
|
|
* The sign of the result is the sign of the number with the larger absolute value.
|
|
* \param[in] a: The first 'floatl' number.
|
|
* \param[in] b: The second 'floatl' number.
|
|
* \return A 'floatl' number representing the result of the subtraction.
|
|
*
|
|
* This function subtracts the absolute values of two 'floatl' numbers. It first extracts
|
|
* the exponents and mantissas of the input numbers. Then it handles special cases
|
|
* such as when the exponents are the same, or when one of the numbers is NaN or Inf.
|
|
* After that, it aligns the mantissas according to the exponent difference, subtracts them,
|
|
* and performs normalization and rounding steps. Finally, it checks for underflow and
|
|
* returns the result with the appropriate sign.
|
|
*/
|
|
static floatl real_floatl_sub(floatl a, floatl b)
|
|
{
|
|
// Extract the exponent of the first number
|
|
uint32_t exp1 = a.exponent;
|
|
// Extract the exponent of the second number
|
|
uint32_t exp2 = b.exponent;
|
|
// Extract the mantissa of the first number, setting sign and exponent to 0
|
|
floatl mant1 = a; mant1.sign = 0; mant1.exponent = 0U;
|
|
// Extract the mantissa of the second number, setting sign and exponent to 0
|
|
floatl mant2 = b; mant2.sign = 0; mant2.exponent = 0U;
|
|
// Initialize the result mantissa to zero
|
|
floatl mant = __FLOATL_INT_ZERO__;
|
|
// Set the sign of the result to the sign of the first number
|
|
bool sign = a.sign;
|
|
// Calculate the difference between the exponents
|
|
int32_t expdiff = exp1 - exp2;
|
|
|
|
// Check if the exponents are the same
|
|
if (expdiff == 0)
|
|
{
|
|
// Return NaN if both exponents represent NaN or Inf
|
|
if (exp1 == __FLOATL_ALL_EXP__.exponent) return __FLOATL_NAN__;
|
|
|
|
// Return zero if the mantissas are equal
|
|
if (floatl_int_ucmp(mant1, mant2) == 0) return __FLOATL_INT_ZERO__;
|
|
|
|
// If the first mantissa is larger
|
|
if (floatl_int_ucmp(mant1, mant2) > 0)
|
|
{
|
|
// Subtract the second mantissa from the first
|
|
mant = floatl_int_sub(mant1, mant2);
|
|
}
|
|
else
|
|
{
|
|
// Subtract the first mantissa from the second and reverse the sign
|
|
mant = floatl_int_sub(mant2, mant1);
|
|
sign = !sign;
|
|
}
|
|
// Shift the result mantissa left by 3 bits
|
|
mant = floatl_int_shl(mant, 3);
|
|
}
|
|
// If the exponent of the first number is smaller
|
|
else if (expdiff < 0)
|
|
{
|
|
// Reverse the sign of the result
|
|
sign = !sign;
|
|
|
|
// Check if the second number is NaN or Inf
|
|
if (exp2 == __FLOATL_ALL_EXP__.exponent) return floatl_nan_inf(sign, mant2);
|
|
// If the first number is denormalized, return the second number with the appropriate sign
|
|
if (exp1 == 0) { b.sign = sign; return b; }
|
|
|
|
exp1 = exp2;
|
|
expdiff = -expdiff;
|
|
// Add the hidden bit to the first mantissa and shift it left by 3 bits
|
|
mant1 = floatl_int_shl(floatl_int_or(mant1, __FLOATL_HIDE_MANT_BIT__), 3);
|
|
// Shift the first mantissa right by the exponent difference, preserving the sticky bit
|
|
mant1 = shift_right_sticky(mant1, expdiff);
|
|
// Add the hidden bit to the second mantissa, shift it left by 3 bits, and subtract the first mantissa
|
|
mant = floatl_int_sub(floatl_int_shl(floatl_int_or(mant2, __FLOATL_HIDE_MANT_BIT__), 3), mant1);
|
|
}
|
|
else
|
|
{
|
|
// Check if the first number is NaN or Inf
|
|
if (exp1 == __FLOATL_ALL_EXP__.exponent) return floatl_nan_inf(sign, mant1);
|
|
// If the second number is denormalized, return the first number with the appropriate sign
|
|
if (exp2 == 0) { a.sign = sign; return a; }
|
|
|
|
// Add the hidden bit to the second mantissa and shift it left by 3 bits
|
|
mant2 = floatl_int_shl(floatl_int_or(mant2, __FLOATL_HIDE_MANT_BIT__), 3);
|
|
// Shift the second mantissa right by the exponent difference, preserving the sticky bit
|
|
mant2 = shift_right_sticky(mant2, expdiff);
|
|
// Add the hidden bit to the first mantissa, shift it left by 3 bits, and subtract the second mantissa
|
|
mant = floatl_int_sub(floatl_int_shl(floatl_int_or(mant1, __FLOATL_HIDE_MANT_BIT__), 3), mant2);
|
|
}
|
|
|
|
// Count the leading zero bits in the result mantissa and adjust for the exponent bits
|
|
int shift = count_leading_zero(mant) - (__FLOATL_EXP_BITS__ - 3);
|
|
// Limit the shift to the current exponent value
|
|
if (shift > exp1)
|
|
{
|
|
shift = exp1;
|
|
// Shift the mantissa right by 1 bit
|
|
mant = floatl_int_shr(mant, 1);
|
|
}
|
|
// Decrease the exponent by the shift amount
|
|
exp1 -= shift;
|
|
// Shift the mantissa left by the shift amount
|
|
mant = floatl_int_shl(mant, shift);
|
|
|
|
// Perform round - to - even rounding on the mantissa
|
|
mant = round_even_grs(mant);
|
|
// Check if the rounded mantissa exceeds the maximum value
|
|
if (floatl_int_ucmp(mant, __FLOATL_MANT_WHL__) > 0)
|
|
{
|
|
// Increment the exponent if the rounded mantissa overflows
|
|
exp1++;
|
|
}
|
|
|
|
// Check for underflow of the exponent
|
|
if (exp1 < 1)
|
|
{
|
|
a = __FLOATL_INT_ZERO__;
|
|
a.sign = sign;
|
|
return a;
|
|
}
|
|
|
|
// Set the result number with the new mantissa, sign, and exponent
|
|
a = mant;
|
|
a.sign = sign;
|
|
a.exponent = exp1;
|
|
|
|
return a;
|
|
}
|
|
|
|
/**
|
|
* \brief Perform addition of two 'floatl' numbers.
|
|
* \param[in] a: The first 'floatl' number to be added.
|
|
* \param[in] b: The second 'floatl' number to be added.
|
|
* \return A 'floatl' number representing the result of the addition.
|
|
*
|
|
* This function decides whether to perform an actual addition or subtraction based on the signs of the two input numbers.
|
|
* If the signs of 'a' and 'b' are the same, it calls the 'real_floatl_add' function to add their absolute values.
|
|
* If the signs are different, it calls the 'real_floatl_sub' function to subtract their absolute values.
|
|
*/
|
|
floatl floatl_add(floatl a, floatl b)
|
|
{
|
|
// Check if the signs of the two numbers are the same
|
|
if (a.sign == b.sign)
|
|
// If the signs are the same, perform addition of absolute values
|
|
return real_floatl_add(a, b);
|
|
// If the signs are different, perform subtraction of absolute values
|
|
return real_floatl_sub(a, b);
|
|
}
|
|
|
|
/**
|
|
* \brief Perform subtraction of two 'floatl' numbers.
|
|
* \param[in] a: The minuend 'floatl' number.
|
|
* \param[in] b: The subtrahend 'floatl' number.
|
|
* \return A 'floatl' number representing the result of the subtraction.
|
|
*
|
|
* This function determines whether to perform an actual subtraction or addition based on the signs of the two input numbers.
|
|
* If the signs of 'a' and 'b' are the same, it calls the 'real_floatl_sub' function to subtract their absolute values.
|
|
* If the signs are different, it calls the 'real_floatl_add' function to add their absolute values.
|
|
*/
|
|
floatl floatl_sub(floatl a, floatl b)
|
|
{
|
|
// Check if the signs of the two numbers are the same
|
|
if (a.sign == b.sign)
|
|
// If the signs are the same, perform subtraction of absolute values
|
|
return real_floatl_sub(a, b);
|
|
// If the signs are different, perform addition of absolute values
|
|
return real_floatl_add(a, b);
|
|
}
|
|
|
|
/**
|
|
* \brief Perform multiplication of two 'floatl' numbers.
|
|
* \param[in] a: The first 'floatl' number to be multiplied.
|
|
* \param[in] b: The second 'floatl' number to be multiplied.
|
|
* \return A 'floatl' number representing the result of the multiplication.
|
|
*
|
|
* This function multiplies two 'floatl' numbers 'a' and 'b'. It first extracts the exponents and mantissas
|
|
* of the input numbers. Then it checks for special cases such as infinity, NaN, and zero. After that,
|
|
* it calculates the new exponent and multiplies the mantissas. It performs normalization, rounding,
|
|
* and checks for underflow and overflow conditions. Finally, it returns the result with the appropriate sign.
|
|
*/
|
|
floatl floatl_mul(floatl a, floatl b)
|
|
{
|
|
// Extract the exponent of the first number
|
|
uint32_t exp1 = a.exponent;
|
|
// Extract the exponent of the second number
|
|
uint32_t exp2 = b.exponent;
|
|
// Extract the mantissa of the first number, setting sign and exponent to 0
|
|
floatl mant1 = a; mant1.sign = 0; mant1.exponent = 0U;
|
|
// Extract the mantissa of the second number, setting sign and exponent to 0
|
|
floatl mant2 = b; mant2.sign = 0; mant2.exponent = 0U;
|
|
// Determine the sign of the result using XOR
|
|
bool sign = a.sign ^ b.sign;
|
|
|
|
// Check if the first number is infinity or NaN
|
|
if (exp1 == __FLOATL_ALL_EXP__.exponent)
|
|
{
|
|
// Check for NaN conditions
|
|
if (floatl_sign(mant1) || ((exp2 == __FLOATL_ALL_EXP__.exponent) && floatl_sign(mant2)) || (exp2 == 0))
|
|
return __FLOATL_NAN__;
|
|
|
|
// Set the result to infinity with the appropriate sign
|
|
a = __FLOATL_INF__;
|
|
a.sign = sign;
|
|
return a;
|
|
}
|
|
|
|
// Check if the second number is infinity or NaN
|
|
if (exp2 == __FLOATL_ALL_EXP__.exponent)
|
|
{
|
|
// Check for NaN conditions
|
|
if (floatl_sign(mant2) || (exp1 == 0))
|
|
return __FLOATL_NAN__;
|
|
|
|
// Set the result to infinity with the appropriate sign
|
|
a = __FLOATL_INF__;
|
|
a.sign = sign;
|
|
return a;
|
|
}
|
|
|
|
// Check if either number is zero
|
|
if ((exp1 == 0) || (exp2 == 0))
|
|
{
|
|
// Set the result to zero with the appropriate sign
|
|
a = __FLOATL_INT_ZERO__;
|
|
a.sign = sign;
|
|
return a;
|
|
}
|
|
|
|
// Initialize the result mantissa to zero
|
|
floatl mant = __FLOATL_INT_ZERO__;
|
|
// Calculate the new exponent
|
|
int32_t exp = exp1 + exp2 - __FLOATL_EXP_MID_VALUE__;
|
|
|
|
// Add the hidden bit (1) to the mantissas
|
|
mant1 = floatl_int_or(mant1, __FLOATL_HIDE_MANT_BIT__);
|
|
mant2 = floatl_int_or(mant2, __FLOATL_HIDE_MANT_BIT__);
|
|
|
|
// Multiply the mantissas and perform right shift with sticky bit preservation
|
|
mant = shift_right_sticky_fl2(mant1, mant2);
|
|
// Check if the result mantissa is less than the maximum allowed value
|
|
if (floatl_int_ucmp(mant, __FLOATL_MANT_PLUS_MAX__) < 0)
|
|
{
|
|
// Shift the mantissa left by 1 bit
|
|
mant = floatl_int_shl(mant, 1);
|
|
}
|
|
else
|
|
{
|
|
// Increment the exponent if the mantissa overflows
|
|
exp++;
|
|
}
|
|
|
|
// Perform round - to - even rounding on the mantissa
|
|
mant = round_even_grs(mant);
|
|
// Check if the rounded mantissa exceeds the maximum value
|
|
if (floatl_int_ucmp(mant, __FLOATL_MANT_WHL__) > 0)
|
|
exp++;
|
|
|
|
// Check for underflow of the exponent
|
|
if (exp < 1)
|
|
{
|
|
// Set the result to zero with the appropriate sign
|
|
a = __FLOATL_INT_ZERO__;
|
|
a.sign = sign;
|
|
return a;
|
|
}
|
|
|
|
// Check for overflow of the exponent
|
|
if (exp > ((uint32_t)__FLOATL_ALL_EXP__.exponent - 1))
|
|
{
|
|
// Set the result to infinity with the appropriate sign
|
|
a = __FLOATL_INF__;
|
|
a.sign = sign;
|
|
return a;
|
|
}
|
|
|
|
// Set the result number with the new mantissa, sign, and exponent
|
|
a = mant;
|
|
a.sign = sign;
|
|
a.exponent = exp;
|
|
|
|
return a;
|
|
}
|
|
|
|
/**
|
|
* \brief Perform division of two 'floatl' numbers.
|
|
* \param[in] a: The dividend 'floatl' number.
|
|
* \param[in] b: The divisor 'floatl' number.
|
|
* \return A 'floatl' number representing the result of the division.
|
|
*
|
|
* This function divides two 'floatl' numbers 'a' by 'b'. It first extracts the exponents and mantissas
|
|
* of the input numbers. Then it checks for special cases such as infinity, NaN, and zero. After that,
|
|
* it calculates the new exponent and performs the division of the mantissas. It performs normalization,
|
|
* rounding, and checks for underflow and overflow conditions. Finally, it returns the result with the appropriate sign.
|
|
*/
|
|
floatl floatl_div(floatl a, floatl b)
|
|
{
|
|
// Extract the exponent of the first number
|
|
uint32_t exp1 = a.exponent;
|
|
// Extract the exponent of the second number
|
|
uint32_t exp2 = b.exponent;
|
|
// Extract the mantissa of the first number, setting sign and exponent to 0
|
|
floatl mant1 = a; mant1.sign = 0; mant1.exponent = 0U;
|
|
// Extract the mantissa of the second number, setting sign and exponent to 0
|
|
floatl mant2 = b; mant2.sign = 0; mant2.exponent = 0U;
|
|
// Determine the sign of the result using XOR
|
|
bool sign = a.sign ^ b.sign;
|
|
|
|
// Check if the first number is infinity or NaN
|
|
if (exp1 == __FLOATL_ALL_EXP__.exponent)
|
|
{
|
|
// Check for NaN conditions
|
|
if (floatl_int_sign(mant1) || (exp2 == __FLOATL_ALL_EXP__.exponent))
|
|
return __FLOATL_NAN__;
|
|
|
|
// Set the result to infinity with the appropriate sign
|
|
a = __FLOATL_INF__;
|
|
a.sign = sign;
|
|
return a;
|
|
}
|
|
|
|
// Check if the second number is infinity or NaN
|
|
if (exp2 == __FLOATL_ALL_EXP__.exponent)
|
|
{
|
|
// Check for NaN conditions
|
|
if (floatl_int_sign(mant2))
|
|
return __FLOATL_NAN__;
|
|
|
|
// Set the result to zero with the appropriate sign
|
|
a = __FLOATL_INT_ZERO__;
|
|
a.sign = sign;
|
|
return a;
|
|
}
|
|
|
|
// Check if the dividend is zero
|
|
if (exp1 == 0)
|
|
{
|
|
// Check for NaN condition (division of zero by zero)
|
|
if (exp2 == 0)
|
|
return __FLOATL_NAN__;
|
|
|
|
// Set the result to zero with the appropriate sign
|
|
a = __FLOATL_INT_ZERO__;
|
|
a.sign = sign;
|
|
return a;
|
|
}
|
|
|
|
// Check if the divisor is zero
|
|
if (exp2 == 0)
|
|
{
|
|
// Set the result to infinity with the appropriate sign
|
|
a = __FLOATL_INF__;
|
|
a.sign = sign;
|
|
return a;
|
|
}
|
|
|
|
// Calculate the new exponent
|
|
int32_t exp = exp1 - exp2 + __FLOATL_EXP_MID_VALUE__;
|
|
|
|
// Add the hidden bit (1) to the mantissas
|
|
mant1 = floatl_int_or(mant1, __FLOATL_HIDE_MANT_BIT__);
|
|
mant2 = floatl_int_or(mant2, __FLOATL_HIDE_MANT_BIT__);
|
|
|
|
floatl mant;
|
|
floatl2 sigl2 = FLOATL2(__FLOATL_INT_ZERO__);
|
|
// Check if the dividend mantissa is less than the divisor mantissa
|
|
if (floatl_int_ucmp(mant1, mant2) < 0)
|
|
{
|
|
// Decrease the exponent
|
|
exp--;
|
|
// Shift the dividend mantissa left by __FLOATL_BIT_PARTS__ bits
|
|
sigl2 = floatl2_int_shl(FLOATL2(mant1), __FLOATL_BIT_PARTS__);
|
|
}
|
|
else
|
|
{
|
|
// Shift the dividend mantissa left by __FLOATL_BIT_PARTS__ - 1 bits
|
|
sigl2 = floatl2_int_shl(FLOATL2(mant1), __FLOATL_BIT_PARTS__ - 1);
|
|
}
|
|
|
|
floatl2 rem;
|
|
// Perform the division of the mantissas and shift the result right
|
|
mant = floatl2_int_shr(floatl2_int_udiv(sigl2, FLOATL2(mant2), &rem), __FLOATL_EXP_BITS__ - 3).low;
|
|
// Check if there is a non - zero remainder
|
|
if (floatl_sign(rem.low))
|
|
{
|
|
// Set the least significant bit of the mantissa
|
|
mant.u32[0] |= 1;
|
|
}
|
|
// Perform round - to - even rounding on the mantissa
|
|
mant = round_even_grs(mant);
|
|
// Check for underflow of the exponent
|
|
if (exp < 1)
|
|
{
|
|
// Set the result to zero with the appropriate sign
|
|
a = __FLOATL_INT_ZERO__;
|
|
a.sign = sign;
|
|
return a;
|
|
}
|
|
|
|
// Check for overflow of the exponent
|
|
if (exp > ((uint32_t)__FLOATL_ALL_EXP__.exponent - 1))
|
|
{
|
|
// Set the result to infinity with the appropriate sign
|
|
a = __FLOATL_INF__;
|
|
a.sign = sign;
|
|
return a;
|
|
}
|
|
|
|
// Set the result number with the new mantissa, sign, and exponent
|
|
a = mant;
|
|
a.sign = sign;
|
|
a.exponent = exp;
|
|
|
|
return a;
|
|
}
|
|
|
|
/**
|
|
* \brief Check if the absolute value of the integer part of a 'floatl' number is greater than 1.
|
|
* \param[in] num: The 'floatl' number to be checked.
|
|
* \return 1 if the absolute value of the integer part is greater than 1 (i.e., the integer part is non - zero), 0 otherwise.
|
|
*
|
|
* This function first checks if the exponent of the 'floatl' number is 1. If so, it returns 0.
|
|
* Then it calculates the actual exponent value by subtracting the mid - value of the exponent range.
|
|
* If the calculated exponent is greater than or equal to 0, it means the integer part is non - zero, and it returns 1.
|
|
*/
|
|
static int LT1(floatl num)
|
|
{
|
|
// If the exponent is 1, the integer part is not greater than 1
|
|
if (((uint32_t)num.exponent) == 1) return 0;
|
|
// Calculate the actual exponent value
|
|
int32_t exp = ((int)num.exponent) - __FLOATL_EXP_MID_VALUE__;
|
|
// Check if the actual exponent is non - negative
|
|
return exp >= 0;
|
|
}
|
|
|
|
/**
|
|
* \brief Check if the absolute value of the fractional part of a 'floatl' number is less than 1.
|
|
* \param[in] num: The 'floatl' number to be checked.
|
|
* \return 1 if the absolute value of the fractional part is less than 1 (i.e., the fractional part is non - zero), 0 otherwise.
|
|
*
|
|
* This function first calculates the actual exponent value by subtracting the mid - value of the exponent range.
|
|
* If the calculated exponent is negative, it means the fractional part is non - zero, and it returns 1.
|
|
* If the exponent is non - negative and less than the number of mantissa bits, it checks each part of the mantissa
|
|
* to see if there are non - zero bits in the fractional part. If so, it returns 1. Otherwise, it returns 0.
|
|
*/
|
|
static int ST1(floatl num)
|
|
{
|
|
// Calculate the actual exponent value
|
|
int exp = ((int)num.exponent) - __FLOATL_EXP_MID_VALUE__;
|
|
|
|
// If the exponent is negative, the fractional part is non - zero
|
|
if (exp < 0) return 1;
|
|
|
|
// If the exponent is less than the number of mantissa bits
|
|
if (exp < __FLOATL_MANT_BITS__)
|
|
{
|
|
// Calculate the number of bits in the fractional part
|
|
int bits = __FLOATL_MANT_BITS__ - exp;
|
|
|
|
// Iterate over each part of the mantissa
|
|
for (int i = 0; i < __FLOATL_MANT_PARTS__; i++)
|
|
{
|
|
if (bits >= 32)
|
|
{
|
|
// If the number of fractional bits is greater than or equal to 32, check if the current part is non - zero
|
|
if (num.mantissas[i] != 0) return 1;
|
|
// Decrease the number of remaining fractional bits
|
|
bits -= 32;
|
|
}
|
|
else
|
|
{
|
|
// Create a mask to check the fractional bits in the current part
|
|
if ((num.mantissas[i] & ((1 << bits) - 1))) return 1;
|
|
// No more fractional bits to check
|
|
bits = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Check the remaining fractional bits in the high - order part of the mantissa
|
|
if (bits && bits <= __FLOATL_MANT_HIGH_BITS__)
|
|
{
|
|
if ((num.mantissa & ((1 << bits) - 1))) return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* \brief Get the character value of the lowest - order digit of the integer part of a 'floatl' number.
|
|
* \param[in] num: The 'floatl' number from which to extract the lowest - order digit of the integer part.
|
|
* \return A character representing the lowest - order digit of the integer part of the 'floatl' number.
|
|
*
|
|
* This function calculates the lowest - order digit of the integer part of a 'floatl' number.
|
|
* It first calculates the exponent value relative to the mid - value of the exponent range.
|
|
* Then, depending on the exponent value, it either returns '0', '1', or calculates the digit
|
|
* through one of two methods (commented out in the code). The current method involves
|
|
* handling the mantissa parts of the 'floatl' number and performing modular arithmetic
|
|
* to obtain the lowest - order digit.
|
|
*/
|
|
static char gChar(floatl num)
|
|
{
|
|
// An array used to handle the last - digit pattern for powers of 2
|
|
const static char bitEnd[4] = {2,4,8,6};
|
|
// Calculate the actual exponent value
|
|
int32_t exp = ((int)num.exponent) - __FLOATL_EXP_MID_VALUE__;
|
|
uint32_t c = 0;
|
|
|
|
// If the exponent is 0, the lowest - order digit of the integer part is 1
|
|
if (exp == 0) return '1';
|
|
// If the exponent is negative, the integer part is 0
|
|
else if (exp < 0) return '0';
|
|
|
|
#if 1 // This block is commented out. It calculates the result by considering each bit
|
|
// Iterate over each bit of the mantissa
|
|
for (int i = 0; i < __FLOATL_MANT_BITS__; i++)
|
|
{
|
|
// Calculate the position relative to the exponent
|
|
int index = i - __FLOATL_MANT_BITS__ + exp;
|
|
if (index >= 0)
|
|
{
|
|
// Determine the part of the mantissa array and the bit position
|
|
int part = i / 32;
|
|
int bit = i % 32;
|
|
// Get the appropriate part of the mantissa
|
|
uint32_t m = part >= __FLOATL_MANT_PARTS__ ? num.mantissa : num.mantissas[part];
|
|
// Check if the bit is set
|
|
if ((m >> bit) & 1)
|
|
{
|
|
// Add the corresponding value to the accumulator
|
|
c += ((index > 0) ? bitEnd[(index - 1) % 4] : 1);
|
|
}
|
|
}
|
|
}
|
|
// Add the value corresponding to the exponent
|
|
c += ((exp > 0) ? bitEnd[(exp - 1) % 4] : 1);
|
|
// Get the last digit and convert it to a character
|
|
c = c % 10 + '0';
|
|
#else // This block is the current method, calculating the result by considering u32 parts
|
|
// Add the hidden bit to the high - order part of the mantissa
|
|
uint32_t hi = (uint32_t)num.mantissa | (1 << __FLOATL_MANT_HIGH_BITS__);
|
|
|
|
// If the exponent is less than or equal to the number of high - order mantissa bits
|
|
if (exp <= __FLOATL_MANT_HIGH_BITS__)
|
|
{
|
|
// Calculate the contribution of the high - order mantissa to the last digit
|
|
c += ((hi >> (__FLOATL_MANT_HIGH_BITS__ - exp)) % 10);
|
|
}
|
|
// If the exponent is greater than the number of high - order mantissa bits
|
|
else
|
|
{
|
|
// Determine the bias based on the exponent
|
|
char bias = bitEnd[(exp - __FLOATL_MANT_HIGH_BITS__ - 1) % 4];
|
|
// Calculate the number of remaining bits
|
|
int bits = exp - __FLOATL_MANT_HIGH_BITS__;
|
|
|
|
// Calculate the contribution of the high - order mantissa with the bias
|
|
c += ((hi * bias) % 10);
|
|
// Iterate over the mantissa array parts
|
|
for (int i = __FLOATL_MANT_PARTS__ - 1; i >= 0 && bits > 0; i--, bits -= 32)
|
|
{
|
|
if (bits > 32)
|
|
// Calculate the contribution of the current mantissa part with the bias
|
|
c += ((num.mantissas[i] * bias) % 10);
|
|
else
|
|
// Calculate the contribution of the relevant bits of the current mantissa part
|
|
c += ((num.mantissas[i] >> (32 - bits)) % 10);
|
|
}
|
|
}
|
|
// Get the last digit and convert it to a character
|
|
c = c % 10 + '0';
|
|
#endif
|
|
|
|
return (char)c;
|
|
}
|
|
|
|
/**
|
|
* \brief Reverse a string of a specified length.
|
|
* \param[in,out] buffer: A pointer to the character array (string) to be reversed.
|
|
* \param[in] len: The length of the string to be reversed.
|
|
*
|
|
* This function takes a character array and its length as input. It swaps the characters
|
|
* from the start and the end of the string towards the middle, effectively reversing the string.
|
|
*/
|
|
static void reverse(char *buffer, int len)
|
|
{
|
|
// Iterate over the first half of the string
|
|
for (int i = 0; i < len / 2; i++)
|
|
{
|
|
// Store the current character in a temporary variable
|
|
char tc = buffer[i];
|
|
// Swap the current character with the corresponding character from the end
|
|
buffer[i] = buffer[len - 1 - i];
|
|
buffer[len - 1 - i] = tc;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \brief Get the decimal character of the lowest - order digit of the integer part of a 'floatl' number
|
|
* and update the 'floatl' number.
|
|
* \param[in,out] num: A pointer to the 'floatl' number. The number will be updated after the operation.
|
|
* \return The character representing the lowest - order digit of the integer part of the 'floatl' number.
|
|
* If the integer part is 0, it returns 0.
|
|
*
|
|
* This function first checks if the integer part of the 'floatl' number is non - zero using the LT1 function.
|
|
* If the integer part is non - zero, it gets the lowest - order digit character using the gChar function.
|
|
* Then it divides the 'floatl' number by 10 to prepare for the next digit extraction.
|
|
*/
|
|
static char floatlIntLowChar(floatl *num)
|
|
{
|
|
char c = 0;
|
|
|
|
// Check if the integer part of the 'floatl' number is non - zero
|
|
if (LT1(*num)) // >= 1, the integer part is not 0
|
|
{
|
|
// Get the character of the lowest - order digit of the integer part
|
|
c = gChar(*num);
|
|
// Divide the 'floatl' number by 10 to move to the next digit
|
|
*num = floatl_div(*num, floatl_from_d(10.0));
|
|
}
|
|
|
|
return c;
|
|
}
|
|
|
|
/**
|
|
* \brief Get the decimal character of the highest - order digit of the fractional part of a 'floatl' number
|
|
* and update the 'floatl' number.
|
|
* \param[in,out] num: A pointer to the 'floatl' number. The number will be updated after the operation.
|
|
* \return The character representing the highest - order digit of the fractional part of the 'floatl' number.
|
|
* If the fractional part is 0, it returns 0.
|
|
*
|
|
* This function first checks if the fractional part of the 'floatl' number is non - zero using the ST1 function.
|
|
* If the fractional part is non - zero, it multiplies the 'floatl' number by 10. Then it gets the highest - order
|
|
* digit character of the new number using the gChar function. Finally, it updates the original 'floatl' number.
|
|
*/
|
|
static char floatlDitHighChar(floatl *num)
|
|
{
|
|
char c = 0;
|
|
floatl temp = *num;
|
|
|
|
// Check if the fractional part of the 'floatl' number is non - zero
|
|
if (ST1(temp)) // The decimal part is not 0
|
|
{
|
|
// Multiply the 'floatl' number by 10 to get the next digit
|
|
temp = floatl_mul(temp, floatl_from_d(10.0));
|
|
// Get the character of the highest - order digit of the new number
|
|
c = gChar(temp);
|
|
// Update the original 'floatl' number
|
|
*num = temp;
|
|
}
|
|
|
|
return c;
|
|
}
|
|
|
|
/**
|
|
* \brief Perform carry addition on a decimal string.
|
|
* \param[in] begin: A pointer to the start of the string.
|
|
* \param[in] end: A pointer to the end of the string.
|
|
* \param[in] format: A format character used to determine if a specific format marker is reached.
|
|
* \return If an exception occurs, returns a pointer to the abnormal character; if the carry is completed, returns NULL;
|
|
* if the format marker is reached, returns a pointer to the format marker.
|
|
*
|
|
* This function traverses the string from the end to the beginning, handling the carry situation of decimal numbers.
|
|
* It skips the decimal point, continues to carry forward when encountering '9', and completes the carry when encountering a space or a valid digit.
|
|
*/
|
|
static char* floatl_carry_add(char *begin, char *end, char format)
|
|
{
|
|
// Start traversing from the end of the string
|
|
char *s = end;
|
|
for (; s >= begin; s--)
|
|
{
|
|
// If the format character is encountered, return a pointer to this character
|
|
if (*s == format) return s;
|
|
// Skip the decimal point
|
|
if (*s == '.') continue;
|
|
// If the current character is '9', set it to '0' and continue to carry forward
|
|
else if (*s == '9')
|
|
{
|
|
*s = '0';
|
|
continue;
|
|
}
|
|
// If the current character is a space, set it to '1' to indicate the completion of the carry
|
|
else if (*s == ' ')
|
|
{
|
|
*s = '1';
|
|
return NULL;
|
|
}
|
|
// If the current character is a digit between '0' and '9', increment it by 1 to complete the carry
|
|
else if (*s >= '0' && *s < '9')
|
|
{
|
|
(*s)++;
|
|
return NULL;
|
|
}
|
|
// If an abnormal character is encountered, return a pointer to this character
|
|
else return s;
|
|
}
|
|
|
|
// If the carry is not completed after traversing the entire string, return s + 1
|
|
return s + 1;
|
|
}
|
|
|
|
/**
|
|
* \brief Perform borrow subtraction on a decimal string.
|
|
* \param[in] begin: A pointer to the start of the string.
|
|
* \param[in] end: A pointer to the end of the string.
|
|
* \param[in] format: A format character used to determine if a specific format marker is reached.
|
|
* \return If an exception occurs, returns a pointer to the abnormal character; if the borrow is completed, returns NULL;
|
|
* if the format marker is reached, returns a pointer to the format marker.
|
|
*
|
|
* This function traverses the string from the end to the beginning, handling the borrow situation of decimal numbers.
|
|
* It skips the decimal point, continues to borrow forward when encountering '0', and completes the borrow when encountering a space or a valid digit.
|
|
*/
|
|
static char* floatl_carry_sub(char *begin, char *end, char format)
|
|
{
|
|
// Start traversing from the end of the string
|
|
char *s = end;
|
|
for (; s >= begin; s--)
|
|
{
|
|
// If the format character is encountered, return a pointer to this character
|
|
if (*s == format) return s;
|
|
// Skip the decimal point
|
|
if (*s == '.') continue;
|
|
// If the current character is '0', set it to '9' and continue to borrow forward
|
|
else if (*s == '0')
|
|
{
|
|
*s = '9';
|
|
continue;
|
|
}
|
|
// If the current character is a space, set it to '9' to indicate the completion of the borrow
|
|
else if (*s == ' ')
|
|
{
|
|
*s = '9';
|
|
return NULL;
|
|
}
|
|
// If the current character is a digit between '1' and '9', decrement it by 1 to complete the borrow
|
|
else if (*s > '0' && *s <= '9')
|
|
{
|
|
(*s)--;
|
|
return NULL;
|
|
}
|
|
// If an abnormal character is encountered, return a pointer to this character
|
|
else return s;
|
|
}
|
|
|
|
// If the borrow is not completed after traversing the entire string, return s + 1
|
|
return s + 1;
|
|
}
|
|
|
|
/**
|
|
* \brief Perform carry addition on a hexadecimal string.
|
|
* \param[in] begin: A pointer to the start of the string.
|
|
* \param[in] end: A pointer to the end of the string.
|
|
* \param[in] format: A format character used to control the case of hexadecimal letters.
|
|
* \return If an exception occurs, returns a pointer to the abnormal character; if the carry is completed, returns NULL.
|
|
*
|
|
* This function traverses the string from the end to the beginning, handling the carry situation of hexadecimal numbers.
|
|
* It skips the decimal point, continues to carry forward when encountering 'F' or 'f', and completes the carry when encountering a space or a valid hexadecimal character.
|
|
*/
|
|
static char* floatl_carry_add_h(char *begin, char *end, char format)
|
|
{
|
|
// Start traversing from the end of the string
|
|
char *s = end;
|
|
for (; s >= begin; s--)
|
|
{
|
|
// Skip the decimal point
|
|
if (*s == '.') continue;
|
|
// If the current character is 'F' or 'f', set it to '0' and continue to carry forward
|
|
else if (*s == 'F' || *s == 'f')
|
|
{
|
|
*s = '0';
|
|
continue;
|
|
}
|
|
// If the current character is a space, set it to '1' to indicate the completion of the carry
|
|
else if (*s == ' ')
|
|
{
|
|
*s = '1';
|
|
return NULL;
|
|
}
|
|
// If the current character is '9', convert it to 'A' or 'a' according to the format
|
|
else if (*s == '9')
|
|
{
|
|
*s = format ? 'A' : 'a';
|
|
return NULL;
|
|
}
|
|
// If the current character is a valid hexadecimal character (digit or letter), increment it by 1 to complete the carry
|
|
else if ((*s >= '0' && *s < '9') ||
|
|
(*s >= 'A' && *s < 'F') ||
|
|
(*s >= 'a' && *s < 'f'))
|
|
{
|
|
(*s)++;
|
|
return NULL;
|
|
}
|
|
// If an abnormal character is encountered, return a pointer to this character
|
|
else return s;
|
|
}
|
|
|
|
// If the carry is not completed after traversing the entire string, return s + 1
|
|
return s + 1;
|
|
}
|
|
|
|
/**
|
|
* \brief Extract a 4 - bit hexadecimal digit from the mantissa of a 'floatl' number at a specified index.
|
|
* \param[in] num: The 'floatl' number from which to extract the hexadecimal digit.
|
|
* \param[in] index: The index indicating the position of the 4 - bit group in the mantissa.
|
|
* \return The 4 - bit hexadecimal digit (0 - 15) if valid, - 1 if the index is out of range.
|
|
*
|
|
* This function extracts a 4 - bit hexadecimal digit from the mantissa of a 'floatl' number at the given index.
|
|
* It first calculates the bit position within the mantissa based on the index. Then it extracts the relevant bits
|
|
* from the appropriate part of the mantissa. If there are insufficient bits in the current part, it may pick up
|
|
* bits from the next part.
|
|
*/
|
|
static char floatl_mant_hex(floatl num, unsigned int index)
|
|
{
|
|
// Calculate the starting bit position of the 4 - bit group
|
|
int bbit = __FLOATL_MANT_BITS__ - 4 - index * 4;
|
|
// Determine the part of the mantissa array where the bit is located
|
|
int part = bbit / 32;
|
|
// Determine the bit position within the part
|
|
int bit = bbit % 32;
|
|
char c = 0;
|
|
|
|
// If the bit position is negative, the index is out of range
|
|
if (bbit < 0) return -1;
|
|
|
|
if (part < __FLOATL_MANT_PARTS__)
|
|
{
|
|
// Extract the 4 - bit group from the current part of the mantissa
|
|
c = (num.mantissas[part] >> bit) & 0xF;
|
|
|
|
/* If there are insufficient bits in the current part, pick up bits from the next part */
|
|
if (bit > 28)
|
|
{
|
|
if (part == __FLOATL_MANT_PARTS__ - 1)
|
|
// If it's the last part, pick up bits from the high - order mantissa
|
|
c |= ((num.mantissa << (32 - bit)) & 0xF);
|
|
else
|
|
// Otherwise, pick up bits from the next part of the mantissa array
|
|
c |= ((num.mantissas[part + 1] << (32 - bit)) & 0xF);
|
|
}
|
|
}
|
|
else
|
|
// If the bit is in the high - order mantissa, extract it directly
|
|
c = (num.mantissa >> bit) & 0xF;
|
|
|
|
return c;
|
|
}
|
|
|
|
/**
|
|
* \brief Convert a 'floatl' number to a string in the %f format.
|
|
* \param[in] num: The 'floatl' number to be converted.
|
|
* \param[out] buffer: A pointer to the character buffer where the converted string will be stored.
|
|
* \param[in] size: The size of the buffer.
|
|
* \param[in] flags: Formatting flags that control the output appearance, such as left - alignment, zero - padding, etc.
|
|
* \param[in] width: The minimum width of the output string.
|
|
* \param[in] precision: The number of digits after the decimal point.
|
|
* \return The length of the converted string.
|
|
*
|
|
* This function converts a 'floatl' number to a string in the %f format. It first extracts the integer and fractional parts of the number.
|
|
* Then it handles the sign prefix, performs right - or left - alignment based on the flags, and rounds the result if necessary.
|
|
*/
|
|
static int floatl_convert_f(floatl num, char *buffer, uint32_t size, uint32_t flags, uint32_t width, uint32_t precision)
|
|
{
|
|
char c = 0;
|
|
int length = 0;
|
|
// Maximum available space in the buffer (excluding the null terminator)
|
|
uint32_t max = size - 1;
|
|
floatl temp;
|
|
char prefix[2] = {0, 0};
|
|
uint32_t prelen = 0;
|
|
// Length of the fractional part (including the decimal point if precision > 0)
|
|
uint32_t postlen = (precision > 0) ? precision + 1 : 0;
|
|
|
|
/* Convert the integer part */
|
|
temp = num;
|
|
// Extract digits of the integer part from the lowest to the highest
|
|
while ((c = floatlIntLowChar(&temp)) > 0) PRINT_CHAR(c);
|
|
|
|
/* The integer part is 0 */
|
|
if (length == 0) PRINT_CHAR('0');
|
|
|
|
/* Handle the sign prefix */
|
|
if (num.sign) prefix[prelen++] = '-'; // Append negative sign
|
|
else if (flags & FLAGS_PLUS) prefix[prelen++] = '+';
|
|
|
|
/* Right - alignment */
|
|
if (!((flags & FLAGS_LEFT)) && (flags & FLAGS_ZEROPAD))
|
|
// Pad with zeros if right - aligned and zero - padding is enabled
|
|
while (length + prelen + postlen < width) PRINT_CHAR('0');
|
|
// Print the sign prefix
|
|
while (prelen > 0) PRINT_CHAR(prefix[--prelen]);
|
|
if (!((flags & FLAGS_LEFT)) && !(flags & FLAGS_ZEROPAD))
|
|
// Pad with spaces if right - aligned and zero - padding is disabled
|
|
while (length + postlen < width) PRINT_CHAR(' ');
|
|
|
|
/* Reverse the integer part */
|
|
reverse(buffer, length);
|
|
|
|
/* Convert the fractional part */
|
|
if (precision > 0)
|
|
{
|
|
/* Decimal point */
|
|
PRINT_CHAR('.');
|
|
|
|
/* Fractional part */
|
|
temp = num;
|
|
// Extract digits of the fractional part from the highest to the lowest
|
|
while (precision && (c = floatlDitHighChar(&temp)) > 0) { PRINT_CHAR(c); precision--; }
|
|
|
|
/* Format alignment */
|
|
// Pad with zeros if the fractional part is shorter than the specified precision
|
|
while (precision--) PRINT_CHAR('0');
|
|
}
|
|
else
|
|
{
|
|
temp = num;
|
|
c = '0';
|
|
}
|
|
|
|
/* If the next digit is valid, continue to calculate the next digit */
|
|
if (c > 0)
|
|
{
|
|
c = floatlDitHighChar(&temp);
|
|
if (c >= '5')
|
|
{
|
|
char *begin = buffer;
|
|
char *end = &buffer[length - 1];
|
|
char *move = NULL;
|
|
if (*begin == '+' || *begin == '-') begin++;
|
|
|
|
// Perform carry addition for rounding
|
|
move = floatl_carry_add(begin, end, 0);
|
|
if (move)
|
|
{
|
|
if (move > begin)
|
|
{
|
|
*(move - 1) = *move;
|
|
}
|
|
else
|
|
{
|
|
PRINT_CHAR('0');
|
|
memmove(move + 1, move, end - move + 1);
|
|
}
|
|
*move = '1';
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Left - alignment */
|
|
if (flags & FLAGS_LEFT)
|
|
// Pad with spaces if left - aligned
|
|
while (length < width) PRINT_CHAR(' ');
|
|
|
|
buffer[length] = 0;
|
|
|
|
return length;
|
|
}
|
|
|
|
/**
|
|
* \brief Convert a 'floatl' number to a string in the %a format.
|
|
* \param[in] num: The 'floatl' number to be converted.
|
|
* \param[out] buffer: A pointer to the character buffer where the converted string will be stored.
|
|
* \param[in] size: The size of the buffer.
|
|
* \param[in] flags: Formatting flags that control the output appearance, such as case, left - alignment, etc.
|
|
* \param[in] width: The minimum width of the output string.
|
|
* \param[in] precision: The number of hexadecimal digits after the decimal point.
|
|
* \return The length of the converted string, or - 2 if there is not enough space for the exponent part.
|
|
*
|
|
* This function converts a 'floatl' number to a string in the %a format, which represents the number in hexadecimal
|
|
* floating - point notation. It first converts the exponent part and stores it at the end of the buffer. Then it
|
|
* handles the integer and fractional parts, taking into account formatting flags such as sign, alignment, and case.
|
|
* Finally, it rounds the result if necessary and moves the exponent part to the appropriate position.
|
|
*/
|
|
static int floatl_convert_a(floatl num, char *buffer, uint32_t size, uint32_t flags, uint32_t width, uint32_t precision)
|
|
{
|
|
char c = 0;
|
|
int length = 0;
|
|
// Maximum available space in the buffer (excluding the null terminator)
|
|
uint32_t max = size - 1;
|
|
floatl temp;
|
|
// Pointer to the position where the exponent part will be stored
|
|
char *expBase = buffer + size;
|
|
char prefix[2] = {0, 0};
|
|
uint32_t prelen = 0;
|
|
uint32_t postlen = 0;
|
|
// Calculate the actual exponent value
|
|
int32_t exp = ((int)num.exponent) - __FLOATL_EXP_MID_VALUE__;
|
|
// Get the hexadecimal digit for rounding
|
|
char carry = floatl_mant_hex(num, precision);
|
|
|
|
/* Convert the exponent part and store it at the end of the buffer */
|
|
if (exp == 0) *(--expBase) = '0';
|
|
else
|
|
{
|
|
int32_t texp = exp;
|
|
if (texp < 0) texp = -texp;
|
|
// Convert the exponent to decimal digits and store them in reverse order
|
|
for ( ; texp != 0; texp /= 10)
|
|
{
|
|
c = texp % 10 + '0';
|
|
*(--expBase) = c;
|
|
if (expBase < buffer + 2) return -2;
|
|
}
|
|
}
|
|
|
|
/* Calculate the remaining length after the integer part */
|
|
postlen = ((precision > 0) ? precision + 1 : 0) + (buffer + size - expBase) + 2; // .d + p+/- + e
|
|
|
|
/* Integer part */
|
|
temp = num;
|
|
if (temp.exponent == 0) PRINT_CHAR('0');
|
|
else PRINT_CHAR('1');
|
|
// Print 'X' or 'x' based on the case flag
|
|
PRINT_CHAR((flags & FLAGS_CASE) ? 'X' : 'x');
|
|
PRINT_CHAR('0');
|
|
|
|
/* Handle the sign prefix */
|
|
if (num.sign) prefix[prelen++] = '-'; // Append negative sign
|
|
else if (flags & FLAGS_PLUS) prefix[prelen++] = '+';
|
|
|
|
/* Right - alignment */
|
|
if (!((flags & FLAGS_LEFT)) && (flags & FLAGS_ZEROPAD))
|
|
// Pad with zeros if right - aligned and zero - padding is enabled
|
|
while (length + prelen + postlen < width) PRINT_CHAR('0');
|
|
// Print the sign prefix
|
|
while (prelen > 0) PRINT_CHAR(prefix[--prelen]);
|
|
if (!((flags & FLAGS_LEFT)) && !(flags & FLAGS_ZEROPAD))
|
|
// Pad with spaces if right - aligned and zero - padding is disabled
|
|
while (length + postlen < width) PRINT_CHAR(' ');
|
|
|
|
/* Reverse the integer part */
|
|
reverse(buffer, length);
|
|
|
|
/* Convert the fractional part */
|
|
if (precision > 0)
|
|
{
|
|
/* Decimal point */
|
|
PRINT_CHAR('.');
|
|
|
|
/* Fractional part */
|
|
temp = num;
|
|
temp.sign = 0;
|
|
if (floatl_int_cmp(temp, __FLOATL_CONST_0__) == 0)
|
|
{
|
|
PRINT_CHAR('0');
|
|
precision--;
|
|
}
|
|
else
|
|
{
|
|
int index = 0;
|
|
// Extract and print hexadecimal digits of the fractional part
|
|
while (precision)
|
|
{
|
|
c = floatl_mant_hex(num, index);
|
|
if (c < 0) break;
|
|
|
|
if (c < 10) c = c + '0';
|
|
else c = (c - 10) + ((flags & FLAGS_CASE) ? 'A' : 'a');
|
|
PRINT_CHAR(c);
|
|
|
|
precision--;
|
|
index++;
|
|
}
|
|
}
|
|
|
|
/* Format alignment */
|
|
// Pad with zeros if the fractional part is shorter than the specified precision
|
|
while (precision--) PRINT_CHAR('0');
|
|
}
|
|
|
|
// Rounding: If the carry digit is greater than 8, perform carry addition
|
|
if (carry > 8)
|
|
{
|
|
char *begin = buffer;
|
|
char *end = &buffer[length - 1];
|
|
if (*begin == '+' || *begin == '-') begin++;
|
|
floatl_carry_add_h(begin, end, (flags & FLAGS_CASE));
|
|
}
|
|
|
|
/* Move the exponent part to the appropriate position */
|
|
// Print 'P' or 'p' based on the case flag
|
|
PRINT_CHAR((flags & FLAGS_CASE) ? 'P' : 'p');
|
|
// Print the sign of the exponent
|
|
PRINT_CHAR(exp < 0 ? '-' : '+');
|
|
// Print the exponent digits
|
|
while (expBase < buffer + size) PRINT_CHAR(*(expBase++));
|
|
|
|
/* Left - alignment */
|
|
if (flags & FLAGS_LEFT)
|
|
// Pad with spaces if left - aligned
|
|
while (length < width) PRINT_CHAR(' ');
|
|
|
|
buffer[length] = 0;
|
|
|
|
return length;
|
|
}
|
|
|
|
/**
|
|
* \brief Convert a 'floatl' number to a string in the %e format.
|
|
* \param[in] num: The 'floatl' number to be converted.
|
|
* \param[out] buffer: A pointer to the character buffer where the converted string will be stored.
|
|
* \param[in] size: The size of the buffer.
|
|
* \param[in] flags: Formatting flags that control the output appearance, such as case, left - alignment, etc.
|
|
* \param[in] width: The minimum width of the output string.
|
|
* \param[in] precision: The number of digits after the decimal point.
|
|
* \return The length of the converted string, or - 2 if there is not enough space for the exponent part.
|
|
*
|
|
* This function converts a 'floatl' number to a string in the %e format, which represents the number in scientific notation.
|
|
* It first extracts the integer and fractional parts of the number, calculates the exponent, and stores the exponent digits at the end of the buffer.
|
|
* Then it handles the sign prefix, performs right - or left - alignment based on the flags, and rounds the result if necessary.
|
|
*/
|
|
static int floatl_convert_e(floatl num, char *buffer, uint32_t size, uint32_t flags, uint32_t width, uint32_t precision)
|
|
{
|
|
char c = 0;
|
|
int length = 0;
|
|
// Maximum available space in the buffer (excluding the null terminator)
|
|
uint32_t max = size - 1;
|
|
floatl temp;
|
|
// Pointer to the position where the exponent part will be stored
|
|
char *expBase = buffer + size;
|
|
char prefix[2] = {0, 0};
|
|
uint32_t prelen = 0;
|
|
uint32_t postlen = 0;
|
|
int offset = 0;
|
|
int32_t exp = 0;
|
|
char *point = NULL;
|
|
|
|
// Buffer to store digits, with extra space for integer and precision digits
|
|
char buf[precision + 2];
|
|
FL_STACK dstack = {buf, sizeof(buf), 0, 0};
|
|
|
|
/* Convert the integer part and push digits onto the stack */
|
|
temp = num;
|
|
while ((c = floatlIntLowChar(&temp)) > 0)
|
|
{
|
|
fl_stack_push(&dstack, c);
|
|
offset++;
|
|
}
|
|
|
|
/* Calculate the exponent of the number */
|
|
temp = num;
|
|
if (offset > 0)
|
|
{
|
|
// If there are integer digits, process the fractional part and set the exponent
|
|
while ((c = floatlDitHighChar(&temp)) > 0) fl_stack_push_h(&dstack, c);
|
|
exp = offset - 1;
|
|
}
|
|
else
|
|
{
|
|
if (num.exponent == 0) exp = 0;
|
|
else
|
|
{
|
|
// If there are no integer digits, find the first non - zero fractional digit and set the exponent
|
|
while ((c = floatlDitHighChar(&temp)) > 0)
|
|
{
|
|
offset++;
|
|
|
|
if (c != '0')
|
|
{
|
|
fl_stack_push_h(&dstack, c);
|
|
while (dstack.size < dstack.capacity && (c = floatlDitHighChar(&temp)) > 0) fl_stack_push_h(&dstack, c);
|
|
exp = -offset;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Pad the stack with zeros if necessary
|
|
while (dstack.size < dstack.capacity) fl_stack_push_h(&dstack, '0');
|
|
|
|
// Pop the first digit and print it
|
|
fl_stack_pop(&dstack, &c);
|
|
PRINT_CHAR(c);
|
|
|
|
/* Convert the exponent part and store it at the end of the buffer */
|
|
if (exp == 0) *(--expBase) = '0';
|
|
else
|
|
{
|
|
int32_t texp = exp;
|
|
if (texp < 0) texp = -texp;
|
|
// Convert the exponent to decimal digits and store them in reverse order
|
|
for ( ; texp != 0; texp /= 10)
|
|
{
|
|
c = texp % 10 + '0';
|
|
*(--expBase) = c;
|
|
if (expBase < buffer + 2) return -2;
|
|
}
|
|
}
|
|
// Ensure the exponent part has at least three digits
|
|
while (buffer + size - expBase < 3) *(--expBase) = '0';
|
|
|
|
/* Calculate the remaining length after the integer part */
|
|
postlen = ((precision > 0) ? precision + 1 : 0) + (buffer + size - expBase) + 2; // .d + e+/- + e
|
|
|
|
/* Handle the sign prefix */
|
|
if (num.sign) prefix[prelen++] = '-'; // Append negative sign
|
|
else if (flags & FLAGS_PLUS) prefix[prelen++] = '+';
|
|
|
|
/* Right - alignment */
|
|
if (!((flags & FLAGS_LEFT)) && (flags & FLAGS_ZEROPAD))
|
|
// Pad with zeros if right - aligned and zero - padding is enabled
|
|
while (length + prelen + postlen < width) PRINT_CHAR('0');
|
|
// Print the sign prefix
|
|
while (prelen > 0) PRINT_CHAR(prefix[--prelen]);
|
|
if (!((flags & FLAGS_LEFT)) && !(flags & FLAGS_ZEROPAD))
|
|
// Pad with spaces if right - aligned and zero - padding is disabled
|
|
while (length + postlen < width) PRINT_CHAR(' ');
|
|
|
|
/* Reverse the integer part */
|
|
reverse(buffer, length);
|
|
|
|
if (precision > 0)
|
|
{
|
|
/* Decimal point */
|
|
PRINT_CHAR('.');
|
|
point = &buffer[length - 1];
|
|
|
|
// Pop digits from the stack and print them as the fractional part
|
|
while (precision && fl_stack_pop(&dstack, &c)) { PRINT_CHAR(c); precision--; }
|
|
}
|
|
|
|
/* Check if rounding is needed */
|
|
if (dstack.size > 0)
|
|
{
|
|
fl_stack_pop(&dstack, &c);
|
|
|
|
// If the next digit is 5 or greater, perform rounding
|
|
if (c >= '5')
|
|
{
|
|
char *move = NULL;
|
|
|
|
// Determine if a carry is needed in the fractional part
|
|
if (point)
|
|
{
|
|
if (floatl_carry_add(point + 1, &buffer[length - 1], '.')) move = point - 1;
|
|
}
|
|
else move = &buffer[length - 1];
|
|
|
|
// If a carry is needed in the integer part
|
|
if (move)
|
|
{
|
|
// If the integer digit can be incremented
|
|
if (*move >= '0' && *move < '9') (*move)++;
|
|
// If the integer digit is 9 and needs to be carried over
|
|
else
|
|
{
|
|
// Set the integer digit to 1
|
|
*move = '1';
|
|
|
|
// Increment the exponent part
|
|
if (exp >= 0) move = floatl_carry_add(expBase, buffer + size - 1, 0);
|
|
else move = floatl_carry_sub(expBase, buffer + size - 1, 0);
|
|
|
|
// If the exponent part needs to be carried over
|
|
if (move)
|
|
{
|
|
if (move <= (buffer + length + 2)) return -2;
|
|
*(--expBase) = '1';
|
|
}
|
|
|
|
// Increment the exponent value
|
|
exp++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Move the exponent part to the appropriate position */
|
|
// Print 'E' or 'e' based on the case flag
|
|
PRINT_CHAR((flags & FLAGS_CASE) ? 'E' : 'e');
|
|
// Print the sign of the exponent
|
|
PRINT_CHAR(exp < 0 ? '-' : '+');
|
|
// Print the exponent digits
|
|
while (expBase < buffer + size) PRINT_CHAR(*(expBase++));
|
|
|
|
/* Left - alignment */
|
|
if (flags & FLAGS_LEFT)
|
|
// Pad with spaces if left - aligned
|
|
while (length < width) PRINT_CHAR(' ');
|
|
|
|
buffer[length] = 0;
|
|
|
|
return length;
|
|
}
|
|
|
|
/**
|
|
* \brief Converts a 'floatl' number to a string.
|
|
*
|
|
* This function takes a 'floatl' number and converts it into a string representation
|
|
* according to a printf-like format string. The resulting string is stored in the provided buffer.
|
|
*
|
|
* \param[in] a: The 'floatl' number to be converted.
|
|
* \param[out] buffer: A pointer to the buffer where the resulting string will be stored.
|
|
* The buffer should be large enough to hold the entire string representation.
|
|
* \param[in] size: The size of the buffer, indicating the maximum number of characters it can hold.
|
|
* \param[in] format: A printf-like format string, structured as [flags][width][type].
|
|
* Flags can be '0' (zero-padding), '-' (left-alignment), '+' (force sign),
|
|
* ' ' (space for positive numbers), '#' (alternate form).
|
|
* Width is a decimal number specifying the minimum width of the output.
|
|
* Type can be 'a' 'A' 'e' 'E' 'f' 'F' 'g' 'G', determining the output format.
|
|
* \return The length of the successfully converted string.
|
|
* Returns 0 if the conversion is invalid.
|
|
* Returns -1 if the 'buffer' pointer is NULL.
|
|
* Returns -2 if the 'size' of the buffer is too small.
|
|
* Returns -3 if the 'format' pointer is NULL.
|
|
*/
|
|
int floatl_print(floatl a, char *buffer, uint32_t size, const char *format)
|
|
{
|
|
int length = 0;
|
|
// Flags used to control the formatting options of the output
|
|
uint32_t flags = 0;
|
|
// Minimum width of the output string
|
|
uint32_t width = 0;
|
|
// Number of digits after the decimal point, default value is 6
|
|
uint32_t precision = 6;
|
|
// Maximum available space in the buffer, excluding the null terminator
|
|
uint32_t max = size - 1;
|
|
|
|
// Check if the buffer pointer is NULL. If so, return -1 indicating an error.
|
|
if (!buffer) return -1;
|
|
// Check if the buffer size is less than 2. If so, return -2 as it's too small to hold a valid string.
|
|
if (size < 2) return -2;
|
|
// Check if the format pointer is NULL. If so, return -3 indicating an error.
|
|
if (!format) return -3;
|
|
|
|
// Skip the '%' character if it's the first character in the format string.
|
|
if (*format == '%') format++;
|
|
|
|
// Parse the format string character by character
|
|
for (; *format; format++)
|
|
{
|
|
// If the length of the converted string is already greater than 0, break the loop.
|
|
if (length > 0) break;
|
|
|
|
// Parse the formatting flags in the format string
|
|
while (1)
|
|
{
|
|
if (*format == '0')
|
|
{
|
|
// Set the zero-padding flag
|
|
flags |= FLAGS_ZEROPAD;
|
|
format++;
|
|
}
|
|
else if (*format == '-')
|
|
{
|
|
// Set the left-alignment flag
|
|
flags |= FLAGS_LEFT;
|
|
format++;
|
|
}
|
|
else if (*format == '+')
|
|
{
|
|
// Set the force sign flag
|
|
flags |= FLAGS_PLUS;
|
|
format++;
|
|
}
|
|
else if (*format == ' ')
|
|
{
|
|
// Set the space for positive numbers flag
|
|
flags |= FLAGS_SPACE;
|
|
format++;
|
|
}
|
|
else if (*format == '#')
|
|
{
|
|
// Set the alternate form flag
|
|
flags |= FLAGS_HASH;
|
|
format++;
|
|
}
|
|
else
|
|
{
|
|
// Exit the loop if no more flags are found
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Convert the width specified in the format string */
|
|
// Parse the characters representing the width in the format string
|
|
while ((*format >= '0') && (*format <= '9'))
|
|
{
|
|
// Convert the digit characters to an integer and update the width value
|
|
width = width * 10U + (unsigned int)(*format++ - '0');
|
|
}
|
|
// Check if the specified width exceeds the available buffer space. If so, return -1.
|
|
if (width > max) return -1;
|
|
|
|
// Check if the precision is specified in the format string
|
|
if (*format == '.')
|
|
{
|
|
// Reset the precision value
|
|
precision = 0;
|
|
format++;
|
|
// Parse the characters representing the precision in the format string
|
|
while ((*format >= '0') && (*format <= '9'))
|
|
{
|
|
// Convert the digit characters to an integer and update the precision value
|
|
precision = precision * 10U + (unsigned int)(*format++ - '0');
|
|
}
|
|
}
|
|
// Check if the specified precision exceeds the available buffer space (minus 2 for possible sign and decimal point). If so, return -1.
|
|
if (precision > max - 2) return -1;
|
|
|
|
/* Distribute the format based on the type specifier */
|
|
// Determine the appropriate conversion function according to the type specifier in the format string
|
|
switch (*format)
|
|
{
|
|
case 'A':
|
|
// Set the case flag for uppercase output
|
|
flags |= FLAGS_CASE;
|
|
case 'a': // Floating-point number in hexadecimal and [P-] notation
|
|
{
|
|
// Call the function to convert the number to hexadecimal floating-point format
|
|
length = floatl_convert_a(a, buffer, size, flags, width, precision);
|
|
}
|
|
break;
|
|
case 'E':
|
|
// Set the case flag for uppercase output
|
|
flags |= FLAGS_CASE;
|
|
case 'e': // Floating-point number in exponential [e-] notation
|
|
{
|
|
// Call the function to convert the number to exponential floating-point format
|
|
length = floatl_convert_e(a, buffer, size, flags, width, precision);
|
|
}
|
|
break;
|
|
case 'G':
|
|
case 'g': // Floating-point number, without displaying meaningless zeros
|
|
case 'F':
|
|
case 'f': // Floating-point number
|
|
{
|
|
// Call the function to convert the number to regular floating-point format
|
|
length = floatl_convert_f(a, buffer, size, flags, width, precision);
|
|
}
|
|
break;
|
|
default:
|
|
// Return 0 if the type specifier is invalid
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// Add a null terminator to the end of the converted string
|
|
buffer[length] = 0;
|
|
|
|
// Return the length of the converted string
|
|
return length;
|
|
}
|