This commit is contained in:
NukeBird 2025-12-08 00:01:39 +03:00
parent 1282fa4523
commit 5736c02eb1
5 changed files with 208 additions and 18 deletions

View file

@ -263,3 +263,171 @@ i8* axl_itoa(i64 val, i8* str, u8 base)
return axl_strrev(str);
}
#if !__STDC_HOSTED__
int _fltused;
#endif // !__STDC_HOSTED__
/*
* @brief Converts float to string with specified precision
*
* @param val: Float value to convert
* @param str: Destination buffer (must be large enough)
* @param precision: Digits after decimal point (0-AXL_F32_MAX_PRECISION)
* @return Pointer to start of string
*
* @note Handles nan, inf, -inf, zero
* @note Uses scientific notation for very large/small numbers
* @note Removes trailing zeros from fractional part
*/
i8* axl_ftoa(f32 val, i8* str, u32 precision)
{
if (!str) return NULL;
// Clamp precision to meaningful range
if (precision > AXL_F32_MAX_PRECISION)
{
precision = AXL_F32_MAX_PRECISION;
}
// Extract float components
union { f32 f; u32 u; } bits = { .f = val };
u32 sign = bits.u >> 31;
u32 exponent = (bits.u >> 23) & 0xFF;
u32 mantissa = bits.u & 0x7FFFFF;
// Handle NaN
if (exponent == 0xFF && mantissa != 0)
{
axl_strcpy(str, "nan");
return str;
}
// Handle Infinity
if (exponent == 0xFF && mantissa == 0)
{
if (sign) axl_strcpy(str, "-inf");
else axl_strcpy(str, "inf");
return str;
}
// Handle zero and denormalized numbers (treat as zero)
if (exponent == 0)
{
i8* p = str;
if (sign) *p++ = '-';
*p++ = '0';
if (precision > 0)
{
*p++ = '.';
for (u32 i = 0; i < precision; i++)
{
*p++ = '0';
}
}
*p = '\0';
return str;
}
i8* start = str;
f32 abs_val = val;
// Apply sign
if (sign)
{
*str++ = '-';
abs_val = -abs_val;
}
// Calculate magnitude for format selection
i32 magnitude = 0;
f32 temp = abs_val;
while (temp >= 10.0f)
{ temp /= 10.0f; magnitude++; }
while (temp > 0.0f && temp < 1.0f)
{ temp *= 10.0f; magnitude--; }
// Use scientific notation for extreme ranges
if (magnitude > 15 || magnitude < -15)
{
// Normalize mantissa to [1, 10)
f32 mant = abs_val;
while (mant >= 10.0f) mant /= 10.0f;
while (mant > 0.0f && mant < 1.0f) mant *= 10.0f;
// Integer part (single digit)
u64 int_part = (u64)mant;
axl_utoa(int_part, str, 10);
str += axl_strlen(str);
// Fractional part
if (precision > 0)
{
*str++ = '.';
f32 frac = mant - (f32)int_part;
for (u32 i = 0; i < precision; i++)
{
frac *= 10.0f;
u32 digit = (u32)frac;
*str++ = '0' + digit;
frac -= (f32)digit;
}
// Trim trailing zeros
while (*(str-1) == '0' && *(str-2) != '.') str--;
}
// Add exponent
*str++ = 'e';
if (magnitude >= 0) *str++ = '+';
axl_itoa(magnitude, str, 10);
return start;
}
// Fixed-point representation for normal range
// Split into integer and fractional parts
u64 int_part = (u64)abs_val;
f32 frac_part = abs_val - (f32)int_part;
// Write integer part
if (int_part == 0)
{
*str++ = '0';
}
else
{
i8 buf[32];
axl_utoa(int_part, buf, 10);
axl_strcpy(str, buf);
str += axl_strlen(buf);
}
// Write fractional part
if (precision > 0)
{
*str++ = '.';
f32 f = frac_part;
u32 i;
for (i = 0; i < precision; i++)
{
f *= 10.0f;
u32 digit = (u32)f;
*str++ = '0' + digit;
f -= (f32)digit;
}
// Remove trailing zeros
while (str > start + 2 && *(str-1) == '0')
{
str--;
}
// Remove decimal point if no fractional digits remain
if (*(str-1) == '.')
{
str--;
}
}
*str = '\0';
return start;
}

View file

@ -14,5 +14,6 @@ i8* axl_strstr(const i8* str, const i8* substr);
i8* axl_strrev(i8* str);
i8* axl_utoa(u64 val, i8* str, u8 base);
i8* axl_itoa(i64 val, i8* str, u8 base);
i8* axl_ftoa(f32 val, i8* str, u32 precision);
#endif // AXL_STRING

View file

@ -8,6 +8,7 @@ typedef signed short i16;
typedef unsigned short u16;
typedef signed int i32;
typedef unsigned int u32;
typedef float f32;
typedef signed long long i64;
typedef unsigned long long u64;
@ -32,4 +33,6 @@ typedef unsigned long long u64;
#define I64_MAX 9223372036854775807LL
#define U64_MAX 18446744073709551615ULL
#define AXL_F32_MAX_PRECISION 6
#endif // !AXL_TYPES_H

7
main.c
View file

@ -4,6 +4,7 @@
#include "axl_string.h"
#include "axl_rle.h"
#include "axl_memory.h"
#include "axl_types.h"
#include <windows.h>
void* memset(void *s, int c, size_t n)
@ -89,6 +90,12 @@ int _start(void)
axl_strcat(text_buff, enc_size_str);
axl_puts(text_buff);
axl_memset(text_buff, '\0', sizeof(text_buff));
axl_ftoa(666.32456f, text_buff, 2);
axl_puts(text_buff);
/*u32 new_len = 0;*/
/*u8 src[] = "Bonjour le monde!";*/
/*u32 src_len = axl_strlen((i8*)src);*/

View file

@ -1,29 +1,40 @@
CC = clang
AXL_SOURCES = $(filter-out main.c %_test.c, $(wildcard *.c))
TEST_EXES = $(patsubst %.c,%.exe,$(wildcard *_test.c))
TEST_CFLAGS = -Wall -Wextra -Werror -pedantic -std=c17 -static -Oz -fsigned-char
CFLAGS = $(TEST_CFLAGS) -nostdlib -ffreestanding -fno-builtin -mno-stack-arg-probe
LDFLAGS = -Wl,/SUBSYSTEM:CONSOLE,/ENTRY:_start -fuse-ld=lld
MINIMAL_CFLAGS = -nostdlib -ffreestanding -fno-builtin -mno-stack-arg-probe \
-fno-stack-protector -fno-unwind-tables -fno-exceptions -fno-rtti \
-fno-asynchronous-unwind-tables -fno-ident
LDFLAGS = -fuse-ld=lld -static \
-Wl,/SUBSYSTEM:CONSOLE \
-Wl,/ENTRY:_start \
-Wl,/OPT:NOICF \
-Wl,/OPT:NOREF \
-Wl,/NODEFAULTLIB \
-Wl,/MERGE:.rdata=.text
LIBS = -lkernel32
SOURCES = $(AXL_SOURCES) main.c
TARGET = prog.exe
all: clean test $(TARGET)
all: clean $(TARGET)
%_test.exe: %_test.c
$(CC) $(TEST_CFLAGS) $(AXL_SOURCES) $< -o $@
test: $(TEST_EXES)
for %%i in ($(TEST_EXES)) do ( \
echo Running %%i && \
%%i || exit /b 1 \
)
test: clean $(TEST_EXES)
@echo "=== Running tests ==="
@for %%i in ($(TEST_EXES)) do (echo ---- %%i ---- && %%i || exit /b 1)
$(TARGET): $(SOURCES)
$(CC) $(CFLAGS) $(SOURCES) -o $(TARGET) $(LDFLAGS) $(LIBS)
$(CC) $(TEST_CFLAGS) $(MINIMAL_CFLAGS) $^ -o $@ $(LDFLAGS) $(LIBS)
clean:
rm -f $(TARGET)
rm -f $(TEST_EXES)
-del /q *.exe *.o 2>nul
.PHONY: all clean test