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

时间:2017-08-08 06:23:06

标签: c string floating-point precision number-formatting

这是我想要做的事情:

  • 我需要打印一个浮点数的小数部分,在用户输入时必须输入 float
  • 小数部分应如下:如果float为43.3423,则输出应为 3423 ;如果数字是45.3400,则输出 3400
  • 使用字符串输入可以轻松完成此操作,但我需要一种方法使其与float一起使用,而不会丢失额外的零或不向用户的原始输入添加零。

这是我已经尝试过的: -

  • 将小数部分乘以frac = num - (int)num,然后乘以frac,直到我们得到零作为余数。但是对于像 34.3400 这样的情况,这种情况就失败了 - 最后两个零点不会包含在这个方法中。
  • 通过

    将浮点数转换为字符串
    char string[20];
    sprintf(string, "%f", float_number);
    

sprintf函数将浮点数作为字符串放置,但此处它也不会自动检测用户输入的精度,并在末尾用额外的零填充字符串(总精度为6)。因此,此处也未获得有关用户原始输入精度的信息。

那么,有没有办法完成这项工作?该号码必须取自用户的浮点号。有没有办法获得用户输入精度的信息?如果不可能,解释将非常有用。

3 个答案:

答案 0 :(得分:1)

我想我明白你来自哪里。例如。在物理学中,无论你是写42.5还是42.500,都会隐含给出有效数字的数量。对于任何42.5x: 42.45 <= x < 42.55代表任意数字42.500x: 42.4995 <= x < 42.5005

对于较大的数字,您可以使用科学记数法1.0e6表示带有x: 950000 <= x < 1050000的数字x。

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

  • 使用的位数(位)仅取决于浮点数的数据类型。如果您的数据类型包含尾数为20位,存储在其中的每个数字都有这20位。在&#34;十进制&#34;之后,尾数始终没有存储。 (二进制?)点,所以你不知道有多少重要的位。
  • 位和小数位之间没有直接映射。您将需要大致 3.5位来表示十进制数字。所以即使您知道多个重要位,您仍然不会知道会产生多少有效十进制数。< / LI>

要解决您的问题,您可以自己存储有效位数:

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

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

答案 1 :(得分:0)

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

答案 2 :(得分:0)

  

必须在用户输入期间作为 float 输入。

     

那么,有没有办法完成这项工作。

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

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

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

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

回想一下float通常有大约6-7个显着的重要前导数字,因此尝试输入“123456789.0”之类的文字会产生float exact 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>
.
.

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

注意:float通常代表约2 32 数字完全
43.3423通常不是其中之一。相反,它的确具有43.3423004150390625

的值

43.3400通常不是其中之一。相反,它的确具有43.340000152587890625

的值