axl_ftoa
This commit is contained in:
parent
1282fa4523
commit
5736c02eb1
5 changed files with 208 additions and 18 deletions
168
axl_string.c
168
axl_string.c
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
7
main.c
|
|
@ -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);*/
|
||||
|
|
|
|||
45
makefile
45
makefile
|
|
@ -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
|
||||
LIBS = -lkernel32
|
||||
SOURCES = $(AXL_SOURCES) main.c
|
||||
TARGET = prog.exe
|
||||
|
||||
all: clean test $(TARGET)
|
||||
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
|
||||
|
||||
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 $(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
|
||||
|
|
|
|||
Loading…
Reference in a new issue