提问人:user20874661 提问时间:12/28/2022 最后编辑:user20874661 更新时间:1/1/2023 访问量:414
修改 itoa 以在输出中将带有十进制点运算符的浮点字符串处理为 ASCII 字符 23(123.456)
Modify itoa to process a floating point string with decimal point operator into ASCII char 23(123.456) in the output
问:
遇到麻烦 C 语言调试步入处理小数点运算符在浮点刺数字中。 示例:1234.5678,itoa() 输出在 4 处停止,并使用 '\0' Null char* 指针终止数组字符串,也是一个数组。因此,即使在添加 '.' 的测试后,我们也只能在 out 数组中得到 1234\0,似乎只是跳过它,就像它实际上不存在一样。感谢您在不使用 printf() 的情况下获取串行 UART 数据的这些计数和浮点小数点的任何想法。
strnlen() 也有同样的问题,只计数到小数点,C sizeof 返回 2 (1234.5678) 个字符的计数放在数组1={0.0} 中,但 sizeof 运算符违反了下面的 clang 定义,因为它只返回字符串的 2 计数。这可能是与供应商相关的 string.h
#if defined(_INLINE) || defined(_STRLEN)
_OPT_IDEFN size_t strlen(const char *string)
{
size_t n = (size_t)-1;
const char *s = string;
do n++; while (*s++);
return n;
}
#endif /* _INLINE || _STRLEN */
size_t len = sizeof *varin;
sizeof 对象和 sizeof(类型名称): 生成一个整数,该整数等于指定对象或类型的大小(以字节为单位)。 (严格来说,sizeof 生成一个无符号整数值,其类型 size_ t 为 在标头 <stddef.h>.)对象可以是变量或数组,或者 结构。类型名称可以是基本类型的名称,如 int 或 double, 或派生类型,如结构或指针。
```
/*****************************************************
*
*! Implementation of itoa()
*
* b\return converted character string
*
***************************************************/
char*
itoa(int16_t num, char* str, int base)//intmax_t
{
uintptr_t i = 0; //int
bool isNegative = false;
/* Handle decimal point explicitely with ASCII (".") */
if(num == '.')
{
//str[i++] = '.';
str[i] = 0x2E;
//
return str;
}
/* Handle unterminated end of string by adding NULL */
else if(num == ' ')
{
str[i] = '\0';
//
return str;
}
/* Handle 0 explicitely, otherwise Null string is printed for 0 */
if(num == '0') //0
{
str[i++] = '0';
//str[i] = '\0';
return str;
}
// In standard itoa(), negative numbers are handled only with
// base 10. Otherwise numbers are considered unsigned.
if (num < 0 && base == 10)
{
isNegative = true;
num = -num;
}
// Process individual digits
while (num != 0)
{
int16_t rem = num % base; //intmax_t
str[i++] = (rem > 9)? (rem-10) + 'a' : rem + '0'; //
num = num/base;
}
// If number is negative, append '-'
if (isNegative)
{
str[i++] = '-';
}
// Append string terminator
str[i] = '\0';
// Reverse the string
reverse(str, i);
return str;
}
```
/* Decls */
static float32_t varin[1] = {0.0};
*varin = dbytes;
static char varout[8];
/* Convert hexadecimal to ASCII codes
* terminate end NULL */
itoa(*varin, varout, 10);
答:
如前所述,您的方法存在许多问题。如果我是你,我会完全平底船。这是一种可能的方法,可能效果更好。请参阅 https://www.geeksforgeeks.org/convert-floating-point-number-string/
Mac_3.2.57$cat asciToFloat.c
// C program for implementation of ftoa()
#include <math.h>
#include <stdio.h>
// Reverses a string 'str' of length 'len'
void reverse(char* str, int len)
{
int i = 0, j = len - 1, temp;
while (i < j) {
temp = str[i];
str[i] = str[j];
str[j] = temp;
i++;
j--;
}
}
// Converts a given integer x to string str[].
// d is the number of digits required in the output.
// If d is more than the number of digits in x,
// then 0s are added at the beginning.
int intToStr(int x, char str[], int d)
{
int i = 0;
while (x) {
str[i++] = (x % 10) + '0';
x = x / 10;
}
// If number of digits required is more, then
// add 0s at the beginning
while (i < d)
str[i++] = '0';
reverse(str, i);
str[i] = '\0';
return i;
}
// Converts a floating-point/double number to a string.
void ftoa(float n, char* res, int afterpoint)
{
// Extract integer part
int ipart = (int)n;
// Extract floating part
float fpart = n - (float)ipart;
// convert integer part to string
int i = intToStr(ipart, res, 0);
// check for display option after point
if (afterpoint != 0) {
res[i] = '.'; // add dot
// Get the value of fraction part upto given no.
// of points after dot. The third parameter
// is needed to handle cases like 233.007
fpart = fpart * pow(10, afterpoint);
intToStr((int)fpart, res + i + 1, afterpoint);
}
}
// Driver program to test above function
int main()
{
char res[20];
float n = 233.5;
ftoa(n, res, 1);
printf("\"%s\"\n", res);
return 0;
}
Mac_3.2.57$cc asciToFloat.c
Mac_3.2.57$./a.out
"233.5"
Mac_3.2.57$
评论
int ipart = (int)n;
因此,当不在范围附近时会失败。考虑将 a 分解为整数和分数部分。intToStr(ipart, res, 0)
n
int
float modff(float value, float *iptr);
float
fpart = fpart * pow(10, afterpoint); intToStr((int)fpart, res + i + 1, afterpoint);
当返回四舍五入到 10 的幂值时,是一个问题。考虑。fpart * pow(10, afterpoint)
float n = 233.999; ftoa(n, res, 1);
fpart = fpart * pow(10, afterpoint)
double
问题是这个MCU编译器中的C认为FP整数字符串的小数点是一个运算符。如果这就是 Borland 在 30++ 年前对 FPU 存在之前以 10 为基数的世界的看法,也许这是表示小数点的唯一方法。我通过尝试创建一个(枚举)列表,其中包含与同名导出变量相关的浮点值,从而发现了这一点。无论如何,FPU 以 10 为基数字符串的小数点不是 C/C++ 运算符!似乎 C 语言没有正确解释 IEEE FPU 规范中 10 基数整数字符串的小数点。
评论
float
发短信很容易做到 - 结果很差。
发短信很难做到 - 效果很好。float
以下是一些问题:
目的地够大吗?
char[]
转换是否处理极值、非数字、无穷大、-0.0、次正常值?如果不是,范围限制是多少?
结果是否四舍五入?四舍五入的规则是什么?
转换有多好?离最佳答案有多近?
我采用了 OP 的总体目标,并且必须对其进行一些修改以形成一个合理的功能。
预先使用以使用无符号数学。|f|
我缩放了浮点数,然后转换为整数,越大越好。 是一个编译器扩展,否则使用并期望减少转换范围并减少。额外的缩放(按 2)允许简单的舍入。unsigned __int128
uintmax_t
ftoa_PREC_MAX
当 较大时,由于分数为 0,因此不需要缩放。float
经过适度测试的代码与 +/-1 ULP 转换文本中的结果文本配合良好。
更好的代码将在 0.5 ULP 以内。边缘情况没有得到很好的锻炼。f
#include <float.h>
#include <math.h>
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#define ftoa_PREC_MAX 15
typedef unsigned __int128 uint_wide; // At least 32 bit
// UINT_WIDE_MAX is the greatest convertible value.
#define UINT_WIDE_MAX_PLUS1 ((-(uint_wide)1 / 2 + 1)*2.0f)
// Convert `float` to a string. Return NULL on error.
char* ftoa(size_t sz, char dest[sz], float f, unsigned prec) {
// Check for pathological parameters.
if (sz == 0 || dest == NULL) {
return NULL;
}
dest[0] = '\0'; // Pre-set dest to "" for error cases.
// First form result in an ample buffer.
// - i digits . f digits \0
char buf[1 + (FLT_MAX_10_EXP + 1) + 1 + ftoa_PREC_MAX + 1];
char *p = buf + sizeof buf;
*--p = '\0';
bool sign = signbit(f);
if (sign) {
f = -f;
}
// Test for more extreme values.
if (!isfinite(f)) {
return NULL;
}
if (prec > ftoa_PREC_MAX) {
return NULL;
}
if (f >= UINT_WIDE_MAX_PLUS1) {
return NULL;
}
// Determine fraction.
uint_wide u;
// If f is large enough, its fractional portion is .000...000.
// No need to scale and potentially overflow.
if (f > 1.0f / FLT_EPSILON) {
u = (uint_wide) f;
for (; prec > 0; prec--) {
*--p = '0';
}
} else {
// Scale by power-of-10 (could use a look-up table instead) and 2.
u = (uint_wide) (f * powf(10, (float) prec) * 2);
// Round, ties up.
u = (u + 1) / 2;
for (; prec > 0; prec--) {
*--p = (char) (u % 10 + '0');
u /= 10;
}
}
*--p = '.';
// Determine whole number part.
do {
*--p = (char) (u % 10 + '0');
u /= 10;
} while (u);
if (sign) {
*--p = '-';
}
// Copy to destination.
size_t len = (size_t) (buf + sizeof buf - p);
if (len > sz) {
return NULL;
}
return strcpy(dest, p);
}
误差的来源包括 10 的幂是近似值,并且乘法可能会产生四舍五入。更高级的解决方案将使用更广泛的 FP 数学运算。f * powf(10, (float) prec)
prec > 9
评论
int2string()
我认为这里有三个独立的问题。让我们依次介绍它们。
C sizeof 返回数组中放入 2 个字符的计数
sizeof
用于计算声明的对象的大小。
它通常不利于确定字符串的长度。
我怀疑你得到了 2,因为你 (a) 试图采用指针的大小,并且 (b) 正在使用 16 位微控制器。您可以
像这样演示该问题:
char *p = "Hello, world!";
printf("size of pointer: %d\n", (int)(sizeof(p)));
printf("length of string: %d\n", (int)(strlen(p)));
sizeof
计算指针本身的大小,同时计算指向字符串的长度。strlen
第二个问题是,你似乎在想象一个数字
— 一个 or 值 — 包含人类可读的
字符,如 或 或 。事实并非如此。一个数字
是一个你可以做数学运算的数字;它不是一串字符
你可以看到。在你的实现中,你正在做这样的事情int
float
'.'
' '
'0'
itoa
if(num == '.')
和
if(num == ' ')
和
if(num == '0')
但这些都是错的。
如果整数的值为 0,则它不包含字符 。
一个浮点变量,在你和我看来,就像它包含一个小数点,
但是在计算机的内存中,它没有,所以它没有意义
测试是否等于 。(它特别没有
sense 因为在该代码中是一个整数变量,而不是浮点数
变量。'0'
float f = 123.456
num
'.'
num
第三个问题是,你试图使用你的函数直接转换一个浮点值,它是
失败,你会对它的失败方式感到惊讶。
但实际上,这并不奇怪。如果你有一个函数itoa
char * itoa(int16_t num, char* str, int base);
接受整数参数,并且您传递一个
浮点值,如 123.456,编译器的第一件事
要做的是将浮点数转换为整数,
通过扔掉小数部分。这就是为什么一切
小数点后似乎不见了。num
因此,抛开这些误解,让我们看看如何
将浮点数正确转换为其字符串表示形式。
事实证明,这是一个非常困难的问题!事实上,一个适当的
在所有情况下执行此任务的算法,没有错误,
直到 1990 年才出版。因此,将
字符串的浮点数通常使用 OR ,因为这些函数有相当大的几率
使用适当的算法。printf
sprintf
但是你说你不想使用 printf。有可能 “手动”将浮点数转换为字符串 — 它可以 实际上相当简单——但你应该意识到这一点 你能写的任何代码都可能无法完美地完成它。
您可以在这个以前的 SO 问题和这个问题中找到有关此问题的一些代码和评论。
这是一个简单、直接、基本上
用于将浮点数转换为
字符串。它写在你现有的函数之上,
正如我认为你试图做的那样。它遵循本评论中建议的方法:
它需要一个浮点数
数字喜欢并将其分解为整数部分和小数部分。它使用 转换整数部分。然后它反复转换并附加小数部分
将其乘以 10。它接受一个参数,告诉它要打印小数点后的数字,就像您可以在 中使用的数字一样。ftoa
itoa
123.456
123
.456
itoa
maxprec
%.6f
printf
char *ftoa(float num, char buf[], int bufsize, int maxprec)
{
char *p = buf;
/* handle negative numbers */
if(num < 0) {
*p++ = '-';
num = -num;
}
/* extract and convert integer part */
int ipart = num;
itoa(num, p, 10);
/* extract and convert fractional part */
float fpart = num - ipart;
p += strlen(p);
*p++ = '.';
int ndig = 0;
char *endp = buf + bufsize - 1; /* leave room for \0 */
/* keep going as long as (a) there's some fractional part left and */
/* (b) we haven't converted too many digits and */
/* (c) we haven't overflowed the destination buffer */
while(fpart != 0 && ndig < maxprec && p < endp) {
fpart *= 10;
int digit = fpart;
*p++ = digit + '0';
fpart = fpart - digit;
ndig++;
}
/* null-terminate and return final string */
*p = '\0';
return buf;
}
下面是一个示例调用:
char buf[20];
printf("%s\n", ftoa(123.456, buf, sizeof(buf), 6));
但请注意,这不是一个非常好的功能!对于“简单”浮点数,它运行得不错,但它有很多问题。示例调用显示了其中之一:它转换为在我的计算机上。(虽然,这实际上并不像看起来那么错误。ftoa(123.456, buf, sizeof(buf), 6)
123.456001
如前所述,正确地将浮点数转换为字符串是一个非常困难的问题,这个简单的代码并没有试图解决该问题的任何困难部分。 请参阅 chux 的答案,了解更完整的方法。
此外,要使它正常工作,您必须至少修复底层函数中的一些问题。
在这种情况下,至少要更改并取消注释该行。您还应该摆脱不需要的 和 大小写,这会导致尝试转换数字 32 和 46 时出现不必要的问题。itoa
if(num == '0')
if(num == 0)
str[i] = '\0'
num == ' '
num == '.'
评论
sizeof
float
sizeof
float
strlen
float
strlen
strlen
float
评论
itoa
itoa
1234.5678
.5678
int
itoa
num == '.'
num == ' '
str[i] = '\0';
do
while
num == 0
itoa
char varout[20]; float f = 123.456; int i = f; printf("f = %.3f, i = %d\n", f, i); itoa(f, varout, 10); printf("%s\n", varout); itoa(i, varout, 10); printf("%s\n", varout);