繁体   English   中英

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

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

这是我想做的事情:

  • 我需要打印浮点数的小数部分,该部分必须在用户输入期间作为浮点输入。
  • 小数部分应类似于:如果float为43.3423,则输出应为3423 如果number为45.3400,则输出应为3400
  • 使用字符串输入可以轻松完成此操作,但是我需要一种使用float进行此工作的方法,而不会丢失额外的零或无需在用户的原始输入中附加零。

这是我已经尝试过的方法:

  • 将小数部分乘以frac = num - (int)num ,然后乘以frac直到得到零。 但这在34.3400之类的情况下失败了-该方法不会包含最后两个零。
  • 将浮点数转换为字符串

     char string[20]; sprintf(string, "%f", float_number); 

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的数字x: 950000 <= x < 1050000

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

  • 使用的位数(位数)仅取决于浮点数的数据类型。 如果您的数据类型的尾数为20位,则其中存储的每个数字都将具有这20位。 尾数始终存储在“十进制”(二进制?)点之后,没有任何部分,因此您将不知道有多少有效位。
  • 在位和十进制数字之间没有直接映射。 您将需要大约 3.5位来代表十进制数字。 因此, 即使您知道许多有效位,您仍然不会知道会产生多少有效十进制数字

为了解决您的问题,您可以将自己的有效数字存储在类似以下的内容中:

struct myNumber
{
    double value;
    int nsignificant;
};

当然,您必须自己解析输入以找出要放在nsignificant 另外, 此处至少使用double作为该值, float的非常有限的精度不会使您走得太远。 这样,您可以使用nsignificant来确定适当的格式字符串,以打印带有所需位数的数字。

这仍然存在上面提到的问题:您不能直接将十进制数字映射到位,因此永远不能保证您的数字可以以您想要的精度存储。 如果精确的十进制表示很重要,则需要使用其他数据类型。 C#提供了一个 ,但C没有提供。 您必须自己实施。 您可以从以下内容开始:

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

在这个结构中,您可以像这样放置1.0e6

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

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

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

在用户输入期间必须将其作为浮点输入。

因此,有没有一种方法可以做到这一点。

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

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;
}

输入输出

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

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


注意: float通常可以精确表示约2 32个数字。
43.3423通常不是其中之一。 取而代之的是它的确切值为43.3423004150390625

43.3400通常不是其中之一。 取而代之的是它的精确值是43.340000152587890625

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM