如何在不丢失用户在 C 中输入的精度的情况下将浮点数转换为字符串?

How to convert float number to string without losing user-entered precision in C?

这是我正在尝试做的事情:


这是我已经尝试过的:-

sprintf 函数将浮点数作为字符串,但这里它也不会自动检测用户输入的精度并在末尾用额外的零填充字符串(总精度为 6)。所以这里也获取不到用户原来输入精度的信息

那么,有什么办法可以做到这一点吗?该数字必须作为用户的浮点数。有什么方法可以获取有关用户输入的精度的信息吗?如果不可能,解释会很有帮助。

我想我明白你的意思。例如。在物理学中,你写42.5还是42.500是有区别的,有效数字的个数是隐式给出的。 42.5 代表任何数字 x: 42.45 <= x < 42.5542.500 代表任何 x: 42.4995 <= x < 42.5005.

对于更大的数字,您将使用科学计数法1.0e6 表示数字 x 为 x: 950000 <= x < 1050000

浮点数使用相同的格式,但使用二进制数字(有时称为位 ;))而不是十进制数字。但有两个重要区别:

  • 使用的位数(位数)仅取决于浮点数的数据类型。如果您的数据类型有例如20位为尾数,每一个存入的数都会有这20位。尾数总是在 "decimal"(二进制?)点之后没有部分存储,因此您将不知道有多少 significant 位。
  • 位和十进制数字之间没有直接映射。您将需要 大约 3.5 位来表示十进制数字。所以 即使你知道 一些 重要的 位,你仍然不知道有多少 重要的小数位 那会让。

为了解决您的问题,您可以自己存储有效数字的数量,如下所示:

struct myNumber
{
    double value;
    int nsignificant;
};

当然,您必须自己解析 输入以找出要放在nsignificant 中的内容。另外,这里至少使用 double 作为值,float 的非常有限的精度不会让你走得太远。有了这个,您可以使用 nsignificant 来确定一个正确的格式字符串,用于打印带有您想要的位数的数字。

这仍然存在上述问题:您不能直接将十进制数字映射到位,因此永远无法保证您的数字可以按照您想要的精度存储。在精确的十进制表示很重要的情况下,您需要为此使用不同的数据类型。 C# provides one,但 C 没有。你必须自己实施它。你可以从这样的事情开始:

struct myDecimal
{
    long mantissa;
    short exponent;
    short nsignificant;
}

在此结构中,您可以例如像这样放置 1.0e6

struct myDecimal x = {
    .mantissa = 1;
    .exponent = 6;
    .nsignificant = 2;
};

当然,这需要您自己编写大量代码来解析和格式化这些数字。

唯一的方法是使用原始字符串值和/或舍入所需的精度创建结构

which has to be input as a float during user input.

So, is there a way to get this done.

差不多。 “技巧”是注意用户输入的文本长度。下面会记住第一个非空白字符的偏移量和数字输入后的偏移量。

scanf(" %n%f%n", &n1, &input, &n2);

n2 - n1 给出代码用户输入的长度来表示float。如果用户输入的是指数表示法、十六进制 FP 表示法、无穷大、非数字、过多的前导零等,则此方法可能会被愚弄。但是对于直接十进制输入效果很好。

想法是将数字打印到至少 n2 - n1 精度的缓冲区,然后确定要打印多少小数部分。

回想一下,float 通常有大约 6-7 个重要的前导数字,因此尝试输入像“123456789.0”这样的文本将导致 float 完全相同 123456792.0 的值,输出将基于该值。

#include <float.h>
#include <math.h>

int scan_print_float(void) {
  float input;
  int n1, n2;
  int cnt = scanf(" %n%f%n", &n1, &input, &n2);
  if (cnt == 1) {
    int len = n2 - n1;
    char buf[len * 2 + 1];
    snprintf(buf, sizeof buf, "%.*f", len, input);
    char dp = '.';
    char *p = strchr(buf, dp);
    if (p) {
      int front_to_dp = p + 1 - buf;
      int prec = len - front_to_dp;
      if (prec >= 0) {
        return printf("<%.*s>\n", prec, p+1);
      }
    }
  }
  puts(".");
  return 0;
}

int main(void) {
  while (scan_print_float()) {
    fflush(stdout);
  }
  return EXIT_SUCCESS;
}

Input/Output

43.3423
<3423>
45.3400
<3400>
-45.3400
<3400>
0.00
<00>
1234.500000
<500000>
.
.

为了稳健地处理这种情况和各种边缘情况,代码应该将用户输入读取为 text 而不是 float.


注意:float通常可以表示大约 232 个数字 正好.
43.3423 通常不是其中之一。相反,它的准确值为 43.3423004150390625

43.3400 通常不是其中之一。相反,它的准确值为 43.340000152587890625