【问题标题】:sscanf("0X80.9", "%f", &value) sets value to a hex float not a floatsscanf("0X80.9", "%f", &value) 将值设置为十六进制浮点数而不是浮点数
【发布时间】:2018-05-07 22:44:12
【问题描述】:

我有一些读取字母值对的代码。可以简化为:

float value = 0.0;
sscanf("0X80.9", "%f", &value);

在 Visual Studio 2013 中,值 = 0

在 Visual Studio 2015 及更高版本中,值 = 128.5625(即十六进制的 0x80 = 十进制的 128)

有人知道为什么会这样吗?更重要的是,有人知道如何让它像以前一样工作吗?

【问题讨论】:

  • 您是用 C 还是 C++ 编程?这些事情可能非常依赖于语言。 C 和 C++不是同一种语言,它们非常不同。
  • @Someprogramerdude 这个特别的东西不能。
  • 它似乎在读取你的十六进制浮点值,因为你写了一个十六进制浮点值;为什么会有问题?
  • 我在 C 和 C++ 中尝试过并得到相同的结果。
  • 这是一个问题,因为实际情况类似于:“W1V0X80”等。我想将“前缀”和值读入字符和相关值。两个版本的编译器之间的行为发生了变化,实际上其他编译器也显示出不同的行为。

标签: c scanf format-specifiers


【解决方案1】:

C99 添加了十六进制浮点常量。 printf() 格式为 %a%A;也有相应的scanf() 格式。但与%f%e%g 一样,scanf() 浮点格式都接受任何浮点格式(因此%f 可以读取使用%e 等打印的输出)。

ISO/IEC 9899:2011 — §6.4.4.2 浮动常量

浮动常数:
十进制浮点常量
十六进制浮点常量

十六进制浮点常量:
hexadecimal-prefix hexadecimal-fractional-constant 二进制指数部分浮动后缀opt
十六进制前缀十六进制数字序列 二进制指数部分浮动后缀opt

十六进制分数常数:
十六进制数字序列opt . 十六进制数字序列
十六进制数字序列 .

二进制指数部分:
p signopt 数字序列
P signopt 数字序列

十六进制数字序列:
十六进制数字
十六进制数字序列十六进制数字

浮动后缀:
之一 flFL

(十六进制前缀当然是0x0X。)

0X80.9 表示法并不完全对应于 C 源代码中所需的十六进制浮点常量表示法,因为没有“二进制指数部分”,但是……

  1. scanf() 系列函数只能推回 1 个字符,所以他们直到为时已晚才知道有问题,所以它最终将其视为最后有一个 P0scanf() 函数根据转换规范读取数据项:

    从流中读取输入项,除非规范包含 n 说明符。一个 输入项定义为输入字符的最长序列,不超过 任何指定的字段宽度,并且是匹配输入序列或匹配输入序列的前缀。285) 输入项之后的第一个字符(如果有)仍然未读。

  2. strtod() 系列函数接受:

    ——一个0x0X,然后是一个非空的十六进制数字序列,可选地包含一个 小数点字符,然后是 6.4.4.2 中定义的可选二进制指数部分;

  3. scanf() 系列函数接受:

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

【讨论】:

  • 标准规定scanf 识别的格式与strtod 识别的格式相同,它明确表示指数部分是可选的。它可能是由 1 个字符的后推引起的,但标准并没有这么说。同样,42 不是浮点常量,但scanf("%f", ...") 接受它(大概是因为它不接受用户敌对)。
  • 好的;是的,这很有趣——谢谢。我添加了一些额外的信息。
【解决方案2】:

C99 增加了一个新特性,十六进制浮点常量。它还为*scanf() 函数添加了新格式。 (源代码中的十六进制浮点常量必须有指数部分;对于scanf,指数是可选的。)

scanf 尽可能多地读取与格式一致的输入。对于 1999 年之前的 C,这是初始的 "0";留下以下"X80.9"。对于 C99,"0X80.9" 有效,并转换为浮点值 128.5625。 (格式与strtof()接受的格式相同。)

C++ 标准通过引用包含了大部分 C 标准库。直到 c++2012 才更新到 C99 版本的 C 库。

Visual Studio 2015 很可能支持 C++12 或更高版本,而 Visual Studio 2013 不支持。

由于新行为现在是标准的,我建议调整您的代码来处理它,而不是试图强制旧行为。如果有办法让 VS2015 以旧方式运行,则应记录在案。

【讨论】:

    【解决方案3】:

    在 1999 C 标准中,添加了 strtod(scanf 中的 %f 所指的内容)的输入可以包含 0x0X 前缀以指示十六进制浮点数。

    16 年后,微软似乎终于开始着手这项工作了。

    您最好的选择是继续使用新行为,而不是尝试调用编译器标志来获取旧行为(这甚至可能都不可能,我不知道)。

    要模拟项目中的旧行为,您可以使用如下代码:

    double my_scan(char const *s)
    {
        double value = 0.0;
    
        if ( ! memcmp(s, "0X", 2) )
             sscanf(s, "%f", &value);
    
        return value;
    }
    

    或任何适合您的用例。也许您最终可能会编写自己的标记器。

    【讨论】:

      猜你喜欢
      • 2014-03-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-08
      • 2023-04-06
      • 2011-12-05
      • 1970-01-01
      • 2016-06-01
      相关资源
      最近更新 更多