【问题标题】:How can I check if a string can be converted to a float?如何检查字符串是否可以转换为浮点数?
【发布时间】:2010-12-28 11:24:23
【问题描述】:

首先,我的上下文是需要将浮点文字(字符串)转换为浮点/双精度值的编译器编写者的上下文。在过去的 15 年里我没有做过任何浮点编程,所以我很确定这是一个完全愚蠢的新手问题。

double res; 
errno=0; 
*res = strtod((const char*)literal,NULL); 
if (errno==ERANGE) throw_a_literal_out_of_range_exception();
return res;

但是c运行时库中没有“strtof”函数?

编辑:澄清。我已经知道字符串 'literal' 是 C 浮点的有效模式。它已经通过了正则表达式测试。我只是想检查是否存在范围/精度问题。

原因是用户可以在 Eiffel 源代码中编写

a := {REAL_32} 3.1415
b := {REAL_32} 3.0E200

将写入的浮点数转换为 32 位浮点数。我认为在第二种情况下,编译器应该检测到值超出范围并引发错误或至少是警告。

【问题讨论】:

  • 为什么需要它是浮点数而不是双精度数?
  • 我认为您应该将文字转换为双精度。然后继续演员阵容。在施法时,您应该检查双精度值是否在浮点数范围内。

标签: c floating-point type-conversion


【解决方案1】:

在 C89 中,您可以使用 sscanf。

例如:

float myfloat;

if(sscanf(str, "%f", &myfloat) != 1)
    /* String could not be converted */
else
    /* String was converted, myfloat is usable */

【讨论】:

    【解决方案2】:

    @尼古拉斯·戈伊: 我认为sscanf(str, "%f, &float) == 1(或!= 1)并没有真正做到我们想要的。

    如果str 中有额外的字符(例如“1.1foo”),它将无错误地解析,这可能是不可取的。这可以通过以下方式纠正:

    char dummy;
    if (sscanf(str, "%f%c", &float, &dummy) != 1) {
       /* Parse error. */
    } else {
       /* Parsed cleanly. */
    }
    

    相反。但是,sscanf%f 很可能在内部使用 strtod 并强制转换为浮动。 C 标准中的语言说:

    a,e,f,g 匹配可选带符号的浮点数、无穷大或 NaN,其 格式与 strtod 的主题序列的预期相同 功能。对应的参数应该是一个指向浮动的指针。

    这暗示了这一点,这对我来说似乎是正确的(FreeBSD 上的 gcc 4.2.1)。使用上面的sscanf 代码,“1.79e308”解析成功,但值为+inf,“5e-300”值为0.0,这些结果与您从(float) 1.79e308 和@987654330 得到的结果相同@。

    无论如何。说了这么多,我质疑为什么 OP 还是要使用 float 而不是 double。

    【讨论】:

    • 因为我正在编写一个 Eiffel 编译器(嗯,它是一个巨大的 Eiffel->C 预处理器)并且用户可以编写“{REAL_32}3.1415”来获得 32 位实数而不是默认的 64 位。我认为如果源代码已经包含精度损失,编译器应该抛出错误或警告。顺便说一句,我已经知道该字符串是纯语法有效的浮点数字符串,我只关心范围和精度错误。抱歉 - 我错过了问题中的这个细节。
    • 好吧,我不知道 OP 希望转换有多严格,但是是的,我的 scanf 代码将忽略尾随字符(如 strtod)。
    • @Nicholas Goy:strtod 不会在尾随字符上返回错误,但您至少可以使用结束指针来验证它是否指向字符串的预期结尾。 scanf 代码与 strtod 相比没有优势。
    【解决方案3】:

    我要说的是,只需使用您已有的代码,但将 strod() 的结果分配给浮点数而不是双精度数。但是你的代码在几个方面是错误的。

    首先,除非发生错误,否则您无法测试 errno。其次, strtod() 不会设置 errno,除非是范围错误。如果你给它传递一个无效的数字,比如“XYZ”,它就不会被设置。

    strtod 更正确的用法是:

    char *p;
    double d = strtod( "123.4xyz", & p );
    if ( * p != 0 ) {
       // not a number - in this case will point at 'x'
    }
    

    使用 strtod() 读取浮点数可能会损失一些精度,但这就是使用浮点数所付出的代价——一般来说,除非你有充分的理由不这样做,否则你应该总是更喜欢使用 double浮动。

    【讨论】:

    • 是的,当然 - 今天下午我似乎遇到了一些问题。谢谢。
    • 好吧,词法分析器已经给了我一个词法有效的数字序列。我唯一要做的就是寻找范围错误。这就是为什么我写了我正在构建一个编译器。我认为当文字值错误并且编译器可以检测到这一点时,它应该发出警告。
    • 在这种情况下,我会采取简单的方法,不支持浮点类型,只支持双精度。浮点数不是特别有用。
    • 不幸的是,我必须实施一个埃菲尔语言标准。太多的第 3 方库在 API 中使用浮点数
    【解决方案4】:

    strtof 在 C89 中不存在,但在 C99 中存在。

    【讨论】:

    • 好吧,我忘了说编译器:MSVC 2005。
    【解决方案5】:

    我建议先转换成双精度,然后再转换成浮点数。如果相对差异 (f-d)/f 大于浮点精度(大致为 1e-7),则数字多于可以安全存储在浮点中的数字。

    【讨论】:

      【解决方案6】:

      由于您在double 中有您的值,您只需检查它是否在float 的范围之外:

      #include <stdlib.h>
      #include <stdio.h>
      #include <float.h>
      
      int main(int argc, char *argv[])
      {
          double d = FLT_MAX;
          if (argc > 1) {
              d = strtod(argv[1], NULL);
          }
          if ((d > 0 && (d > FLT_MAX || d < FLT_MIN))
                      || (d < 0 && (d < -FLT_MAX || d > -FLT_MIN)))
              printf("Invalid float: %g\n", d);
          else
              printf("Valid: %g\n", d);
      
          return EXIT_SUCCESS;
      }
      

      运行程序:

      $ ./a.out
      Valid: 3.40282e+38
      $ ./a.out 3.5e38
      Invalid float: 3.5e+38
      $ ./a.out -1e40
      Invalid float: -1e+40
      

      您可能需要也可能不需要为正确的strtod() 返回添加测试,这取决于double 类型是否也存在溢出的可能性。

      【讨论】:

        【解决方案7】:

        在 MSVC 上,您可以使用_atoflt(),在 stdlib.h 中定义

        【讨论】:

          猜你喜欢
          • 2010-10-18
          • 2021-01-28
          • 2012-09-10
          • 2022-10-15
          • 1970-01-01
          • 2016-10-07
          • 1970-01-01
          • 2011-07-26
          • 1970-01-01
          相关资源
          最近更新 更多