Feat: Printf support float format.

This commit is contained in:
Yaksis 2024-05-23 17:58:21 +08:00
parent 05cea96bc8
commit d74fdf36d5
No known key found for this signature in database
GPG key ID: F27FB4A20C9CCAC0

View file

@ -7,6 +7,13 @@
static inline int isdigit(int ch) { return (ch >= '0') && (ch <= '9'); } static inline int isdigit(int ch) { return (ch >= '0') && (ch <= '9'); }
static size_t strnlen(const char *s, size_t count)
{
const char *sc;
for (sc = s; *sc != '\0' && count--; ++sc);
return sc - s;
}
static int skip_atoi(const char **s) { static int skip_atoi(const char **s) {
int i = 0; int i = 0;
@ -103,10 +110,167 @@ static char *number(char *str, long num, int base, int size, int precision,
return str; return str;
} }
#define SZ_NUM_BUF 32
static char sprint_fe[SZ_NUM_BUF+1];
static int ilog10(double n) /* 在整数输出中计算 log10(n) */
{
int rv = 0;
while (n >= 10) { /* 右移小数位 */
if (n >= 100000) {
n /= 100000;
rv += 5;
} else {
n /= 10;
rv++;
}
}
while (n < 1) { /* 左移小数位 */
if (n < 0.00001) {
n *= 100000;
rv -= 5;
} else {
n *= 10;
rv--;
}
}
return rv;
}
// 整数位数
static double i10x(int n) /* 计算 10^n 的整数输入 */
{
double rv = 1;
while (n > 0) {
if (n >= 5) {
rv *= 100000;
n -= 5;
} else {
rv *= 10;
n--;
}
}
while (n < 0) { /* Right shift */
if (n <= -5) {
rv /= 100000;
n += 5;
} else {
rv /= 10;
n++;
}
}
return rv;
}
// 浮点数据转字符串%f,%e,%E
// char* buf, 字符串缓存
// double val, 浮点数
// int prec, 小数位数
// char fmt 类型标识
static void ftoa(char *buf, double val, int prec, char fmt) {
int d;
int e = 0, m = 0;
char sign = 0;
double w;
const char *er = 0;
const char ds = '.';
unsigned int *pu = (unsigned int *)&val;
// 阶码全 1尾数不为 0不存在的数"NaN"
if (((pu[1] & 0x7ff00000) == 0x7ff00000) &&
(((pu[1] & 0xfffff) != 0) || (pu[0] != 0))) {
er = "NaN";
} else {
// 默认 6 位小数
if (prec < 0)
prec = 6;
// 符号处理
if (val < 0) {
val = 0 - val;
sign = '-';
} else {
sign = '+';
}
// 阶码全 1尾数部分全 0符号位为 0 表示正无穷。
// 阶码全 1尾数部分全 0符号位为 1 表示负无穷。
if (((pu[1] & 0x7fffffff) == 0x7ff00000) && (pu[0] == 0)) {
er = "INF";
}
//
else {
// f类型
if (fmt == 'f') {
val += i10x(0 - prec) / 2; /*用于四舍五入,比如 1.67 保留 1 位小数为 1.7 */
m = ilog10(val); /*整数位数*/
if (m < 0)
m = 0;
if (m + prec + 3 >= SZ_NUM_BUF)
er = "OV"; /* 最大缓存 */
} else { /* E 类型 x.xxxxxxe+xx*/
if (val != 0) {
val += i10x(ilog10(val) - prec) /
2; /* 用于四舍五入prec 表示底数部分的小数位数*/
e = ilog10(val); /*整数位数*/
if (e > 99 || prec + 7 >= SZ_NUM_BUF) //指数范围,及最大缓存,
{
er = "OV";
} else {
if (e < -99)
e = -99;
val /= i10x(e); /* 计算底数 */
}
}
}
}
// 有效浮点
if (!er) {
// 符号位
if (sign == '-')
*buf++ = sign;
do {
/* 进入小数部分时插入小数分隔符 */
if (m == -1)
*buf++ = ds;
//剪掉最高的数字 d
w = i10x(m);
//
d = (int)(val / w);
val -= d * w;
*buf++ = (char)('0' + d); /* 记录 10 进制 */
} while (--m >= -prec);
if (fmt != 'f') {
*buf++ = (char)fmt;
if (e < 0) {
e = 0 - e;
*buf++ = '-';
} else {
*buf++ = '+';
}
*buf++ = (char)('0' + e / 10);
*buf++ = (char)('0' + e % 10);
}
}
}
// 特殊值
if (er) { // 符号
if (sign)
*buf++ = sign;
// 数据字符串
do {
*buf++ = *er++;
} while (*er);
}
*buf = 0; /* 结束符 */
}
int vsprintf(char *buf, const char *fmt, va_list args) { int vsprintf(char *buf, const char *fmt, va_list args) {
int len; int len;
unsigned long num; unsigned long num;
int i, base; int i, j, base;
char *str; char *str;
const char *s; const char *s;
@ -196,7 +360,7 @@ int vsprintf(char *buf, const char *fmt, va_list args) {
case 's': case 's':
s = va_arg(args, char *); s = va_arg(args, char *);
len = strlen(s); len = strnlen(s, precision);
if (!(flags & LEFT)) if (!(flags & LEFT))
while (len < field_width--) while (len < field_width--)
@ -226,6 +390,25 @@ int vsprintf(char *buf, const char *fmt, va_list args) {
} }
continue; continue;
case 'f': /* Floating point (decimal) */
case 'e': /* Floating point (e) */
case 'E': /* Floating point (E) */
// double 数据转字符串
ftoa(sprint_fe, va_arg(args, double), precision, *fmt); /* 浮点转字符串*/
// 右对齐 左侧补充空格
if (!(flags & LEFT)) {
for (j = strnlen(sprint_fe, SZ_NUM_BUF); j < field_width; j++)
*str++ = ' ';
}
// 数据主体
i = 0;
while (sprint_fe[i])
*str++ = sprint_fe[i++]; /* 主体 */
// 左对齐 右侧补充空格
while (j++ < field_width)
*str++ = ' ';
continue;
case '%': case '%':
*str++ = '%'; *str++ = '%';
continue; continue;