修改 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

提问人:user20874661 提问时间:12/28/2022 最后编辑:user20874661 更新时间:1/1/2023 访问量:414

问:

遇到麻烦 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);

调试发送到 itoa() 的 in/out 数组

Strlen Itoa 的浮点 十进制 大小

评论

2赞 Steve Summit 12/28/2022
你的函数看起来大部分都会成功地将整数转换为字符串,这确实是 的工作。但是你不能像在整数中那样存储浮点数!整数的定义特征之一是不能有小数部分。当您尝试将该浮点数作为 传递/分配时,该部分会丢失,而不是由于代码中的任何错误。itoaitoa1234.5678.5678intitoa
2赞 Steve Summit 12/28/2022
一些提示:(1)摆脱测试。这是毫无意义的,只会阻止您正确转换整数 46。(2)去掉测试,这也是没有意义的。(3)将注释掉的行放回去;你确实需要它们。(4)如果使用/循环,则可以去掉测试。num == '.'num == ' 'str[i] = '\0';dowhilenum == 0
0赞 user20874661 12/28/2022
大家好,我在两个 itoa() 数组中添加了调试输出链接和 strln() 调用和贴花。调试链接(上面)似乎推断编译器没有添加空格字符,而是为浮点小数点添加“\0”。在我看来,没有理由这样做。在查看调试链接时,对整数类型 FPU 数据的过时和正确处理似乎更可疑。itoa 可以轻松处理输入数组中的空格,但只会将 NULL in 作为 NULL out 处理。编译器 NULL 为小数点,而不是将其传递到输入数组字符串中,如调试输出链接所示;-)
0赞 Steve Summit 12/29/2022
感谢您提供额外的代码,但再次:您不能使用此代码转换浮点数。这毫无意义。另外,我不确定您所说的“输入数组”是什么意思。函数的输入是整数,而不是数组。它不包含任何字符 - 空格、小数点或其他。itoa
0赞 Steve Summit 12/29/2022
请编译并运行以下代码: 这应该希望能给你一些关于实际情况的提示。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);

答:

-1赞 Andrew 12/29/2022 #1

如前所述,您的方法存在许多问题。如果我是你,我会完全平底船。这是一种可能的方法,可能效果更好。请参阅 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$

评论

0赞 chux - Reinstate Monica 12/30/2022
int ipart = (int)n;因此,当不在范围附近时会失败。考虑将 a 分解为整数和分数部分。intToStr(ipart, res, 0)nintfloat modff(float value, float *iptr);float
0赞 chux - Reinstate Monica 12/30/2022
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);
0赞 Andrew 12/30/2022
@是有道理的;另外:n = 233.4,将返回 n = 233.3--hashing out 此错误的详细信息作为 OP 的练习。
0赞 chux - Reinstate Monica 12/30/2022
在审查中,不太可能经历建议的综述,因为乘法是用数学完成的。fpart = fpart * pow(10, afterpoint)double
0赞 user20874661 12/31/2022
整个问题是必须编写 char* 字符串来处理带有不可见运算符点的浮点整数字符串。char* 不会读取运算符点的右侧,并且十进制积分测试的编译器不会进行类型转换以匹配浮点数。毕竟,这是一个浮点小数点 FPU 数学,而不是浮动运算符 FPU 数学。
-1赞 user20874661 12/30/2022 #2

问题是这个MCU编译器中的C认为FP整数字符串的小数点是一个运算符。如果这就是 Borland 在 30++ 年前对 FPU 存在之前以 10 为基数的世界的看法,也许这是表示小数点的唯一方法。我通过尝试创建一个(枚举)列表,其中包含与同名导出变量相关的浮点值,从而发现了这一点。无论如何,FPU 以 10 为基数字符串的小数点不是 C/C++ 运算符!似乎 C 语言没有正确解释 IEEE FPU 规范中 10 基数整数字符串的小数点。

评论

2赞 Steve Summit 12/31/2022
您能否提供任何证据证明“编译器认为 FP 整数字符串的小数点是运算符”?我知道这些年来有一些奇怪的编译器,但我很难相信这一点。
0赞 user20874661 1/4/2023
这就是 C 语言将点定义为多运算符的方式。点还可用于形成具有结构名称标签键的复合变量。然而,当 (float) 在任意数量的整数前面加上内联某处的点时,它应该自动将运算符点格式化为 FPU 的 ASCII 句点0x23。汇编程序跟踪显示 ALU 是在带有 FPU 调用标记的 (float) 指令之后调用的。C 语言 MCU 需要特殊的编译器处理。无需 ALU 参与将浮点字符串作为整数传递。小数点是单个 itoa() 测试检测到的 ASCII 十进制 46。
1赞 chux - Reinstate Monica 1/1/2023 #3

float发短信很容易做到 - 结果很差。
发短信很难做到 - 效果很好。
float

以下是一些问题:

  • 目的地够大吗?char[]

  • 转换是否处理极值、非数字、无穷大、-0.0、次正常值?如果不是,范围限制是多少?

  • 结果是否四舍五入?四舍五入的规则是什么?

  • 转换有多好?离最佳答案有多近?


我采用了 OP 的总体目标,并且必须对其进行一些修改以形成一个合理的功能。

预先使用以使用无符号数学。|f|

我缩放了浮点数,然后转换为整数,越大越好。 是一个编译器扩展,否则使用并期望减少转换范围并减少。额外的缩放(按 2)允许简单的舍入。unsigned __int128uintmax_tftoa_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

评论

0赞 chux - Reinstate Monica 1/5/2023
@user20874661 代码在哪里?int2string()
0赞 user20874661 1/5/2023
@Monica,安德鲁将它发布在你最新发布的代码下方。
0赞 user20874661 1/5/2023
请注意,下面的 fdtoa() 使用了对 powf() 的快速 RTS 调用,编译器使用了 double 并在 C 运算符点处分离了 float32 输入。这是让strlen()返回点右边字符串长度的另一个问题。输出不能使用 printf() 来格式化字符串,因为内联调用会向 UART TXD 发送 HID 命令,然后将数据发送到目标小部件。
0赞 user20874661 1/5/2023
这些编译器开关会停止 C2000 MCU powf() 错误:--float_operations_allowed=all 认为这应该是默认模式。• 如果 --tmu_support=tmu1 与 --fp_mode=relaxed 一起使用,则使用以下 32 位 RTS 数学函数的特殊“宽松”版本:exp2f()、expf()、log2f()、logf() 和 powf()。请注意,不提供使用双精度类型的宽松版本
1赞 Steve Summit 1/1/2023 #4

我认为这里有三个独立的问题。让我们依次介绍它们。

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 值 — 包含人类可读的 字符,如 或 或 。事实并非如此。一个数字 是一个你可以做数学运算的数字;它不是一串字符 你可以看到。在你的实现中,你正在做这样的事情intfloat'.'' ''0'itoa

if(num == '.')

if(num == ' ')

if(num == '0')

但这些都是错的。 如果整数的值为 0,则它不包含字符 。 一个浮点变量,在你和我看来,就像它包含一个小数点, 但是在计算机的内存中,它没有,所以它没有意义 测试是否等于 。(它特别没有 sense 因为在该代码中是一个整数变量,而不是浮点数 变量。'0'float f = 123.456num'.'num

第三个问题是,你试图使用你的函数直接转换一个浮点值,它是 失败,你会对它的失败方式感到惊讶。 但实际上,这并不奇怪。如果你有一个函数itoa

char * itoa(int16_t num, char* str, int base);

接受整数参数,并且您传递一个 浮点值,如 123.456,编译器的第一件事 要做的是将浮点数转换为整数, 通过扔掉小数部分。这就是为什么一切 小数点后似乎不见了。num


因此,抛开这些误解,让我们看看如何 将浮点数正确转换为其字符串表示形式。 事实证明,这是一个非常困难的问题!事实上,一个适当的 在所有情况下执行此任务的算法,没有错误, 直到 1990 年才出版。因此,将 字符串的浮点数通常使用 OR ,因为这些函数有相当大的几率 使用适当的算法。printfsprintf

但是你说你不想使用 printf。有可能 “手动”将浮点数转换为字符串 — 它可以 实际上相当简单——但你应该意识到这一点 你能写的任何代码都可能无法完美地完成它。

您可以在这个以前的 SO 问题这个问题中找到有关此问题的一些代码和评论。

这是一个简单、直接、基本上 用于将浮点数转换为 字符串。它写在你现有的函数之上, 正如我认为你试图做的那样。它遵循本评论中建议的方法: 它需要一个浮点数 数字喜欢并将其分解为整数部分和小数部分。它使用 转换整数部分。然后它反复转换并附加小数部分 将其乘以 10。它接受一个参数,告诉它要打印小数点后的数字,就像您可以在 中使用的数字一样。ftoaitoa123.456123.456itoamaxprec%.6fprintf

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 时出现不必要的问题。itoaif(num == '0')if(num == 0)str[i] = '\0'num == ' 'num == '.'

评论

0赞 user20874661 1/4/2023
根据我的 C 语言手册,sizeof() 应该返回 Array 中的整数数,但实际上返回十六进制字符数,直到浮点数中的点。它可以完美地检查结构中关键元素的数量。strlen() 有同样类型的浮点问题,但实际上返回到点的整数数。
0赞 user20874661 1/4/2023
while(fpart != 0 && ndig < maxprec && p < endp) {~~~~~~~~~~~~~~~~~~} 问题是编译器不会按照 C 语言定义的那样转换表达式的 Itoa() 非浮点部分,而不会产生链接错误。我在 TI C2000 论坛上发布了这个问题。即使我们使方程值 x.x 仍然不会将两个分子的整数转换为相同类型。也不能将 fprint() 转换为 UART FIFO,因为 ftoa() 是内联的,需要通过以 10 为基数的十六进制数字转换为 ASCII 十六进制数字。
0赞 Steve Summit 1/4/2023
@user20874661 你试过我发布的代码吗?
0赞 Steve Summit 1/4/2023
@user20874661 不返回“数组中的整数数”。它返回任何数据对象的大小(以字节为单位)。应用于对象时,不会返回“十六进制字符数,直到点”,而是返回 4,在大多数系统上。(中没有点。同样没有意义的是,将 : 专门应用于字符串,而不是其他数据类型的对象。 无法返回“到点的整数数”,因为值中没有整数、字符或点。sizeoffloatsizeoffloatstrlenfloatstrlenstrlenfloat
0赞 Steve Summit 1/4/2023
@user20874661它应该在不产生链接错误的情况下进行 请发布这些错误的文本以及生成这些错误的代码。