【问题标题】:How to convert float number to string without losing user-entered precision in C?如何在不丢失 C 中用户输入的精度的情况下将浮点数转换为字符串?
【发布时间】:2018-01-15 13:46:38
【问题描述】:

这是我想要做的:

  • 我需要打印一个浮点数的小数部分,在用户输入过程中该浮点数必须输入为 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 个精度)。所以这里也没有得到用户原始输入精度的信息。

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

【问题讨论】:

  • 不能这样做,因为浮点值不会保留有关其创建方式和输入精度的信息。浮点值也存在自然舍入问题,因此用户输入的值可能无法用float 精确表示。简而言之,不使用字符串作为输入是不可能的。
  • 牢记@Someprogrammerdude 的提示...您可以将输入作为字符串,计算小数点后的数字,然后将字符串输入转换为浮点数(例如使用atof()strtod() ),并使用计数的数字进行输出格式化。这听起来需要付出很大的努力(大量的字符摆弄和许多应该进行错误检查的地方)。 会三思而后行“漂亮的输出”是否值得付出努力...
  • 你说得对,实际上我只是想找出是否有办法通过将初始输入本身作为浮点数来检测用户输入的精度,但无论如何我都明白了。谢谢。
  • 你需要的是保持精度的十进制浮点数; Python 编程语言有这些。使用 C 中经常使用的 IEEE 754 二进制浮点数是不容易实现的。

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


【解决方案1】:

我想我明白你来自哪里。例如。在物理学中,写42.542.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 位。尾数总是在“十进制”(二进制?)点之后不加任何部分地存储,因此您不会知道有多少有效位。
  • 位和十进制数字之间没有直接映射。您将需要大约 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;
};

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

【讨论】:

    【解决方案2】:

    在用户输入过程中必须以 float 的形式输入。

    那么,有没有办法做到这一点。

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

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

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

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

    回想一下,float 通常有大约 6-7 个有效前导数字,因此尝试输入像“123456789.0”这样的文本将导致float 精确值为@987654327 @ 并且输出将基于该值。

    #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 通常可以表示大约 232 个数字完全正确
    43.3423 通常不是其中之一。相反,它的准确值为43.3423004150390625

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

    【讨论】:

      【解决方案3】:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-05-26
        • 1970-01-01
        • 1970-01-01
        • 2016-09-11
        • 1970-01-01
        • 2018-08-12
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多