【问题标题】:scanf is successful but the value stored is not right for some large floating point numbers [closed]scanf 成功,但存储的值不适用于某些大浮点数 [关闭]
【发布时间】:2017-03-07 13:49:38
【问题描述】:

为以下代码输入大数字会导致scanf 成功,尽管该值未正确存储。

printf("\nDouble max: %f\n", DBL_MAX);
printf("\nFloat max: %f\n", FLT_MAX);
printf("\nPlease insert root1 data: ");
float input1;
scanResult = scanf("%f", &input1);
printf("\nScan Result is %d\n", scanResult);

double input2;
printf("\nPlease insert root2 data: ");
scanResult = scanf("%lf", &input2);
printf("\nScan Result is %d\n", scanResult);

printf("%f", input1);
printf("%f", input2);

输出:

Double max: 17976931348623157000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000
0.000000

Float max: 340282346638528860000000000000000000000.000000

Please insert root1 data: 1872389723948273985723984756982375698374568

Scan Result is 1

Please insert root2 data: 928734812348721834.2348275

Scan Result is 1


1.#INF00

928734812348721790.000000

所以我的问题是:

  1. 第一次扫描:为什么scanf 仍然说扫描结果是1,而你可以看到该值没有正确存储?

  2. 第二次扫描:为什么会这样舍入,我该如何解决?

  3. 一般而言:我们可以看到,通过 0 的数量,DBL_MAXFLT_MAX 比我给出的输入要大得多。那么为什么它不能正确存储输入呢?

【问题讨论】:

  • 你需要一本更好的c 书(或网站)
  • @KevinDTimm 我可以理解为什么吗?
  • +1 关于 scanf() 溢出处理的有趣问题。它不仅抵消了您在验证输入时缺乏勤奋;-)。
  • 因为没有书或网站会指示您声明浮点变量,并且他们尝试使用 printf() 的形式。 (注意:您已经编辑了帖子,因此绝对错误的版本不再可见)
  • 正如我之前写的,它是从一个从 void* 转换它的函数复制而来的。

标签: c floating-point double scanf numeric-limits


【解决方案1】:
  1. 第一次扫描:为什么scanf 仍然说扫描结果为1,而您可以看到该值未正确存储?

扫描结果为 1,因为匹配、转换和分配了一个输入项。这就是scanf()的返回值非负时的含义。

此外,我没有看到该值存储不正确。您提供了一个数字序列,解释为十进制数,大于 float 可以表示的最大值。 scanf() 函数将这些输入转换为float 作为代表系统可以表示的最大float“值”的数字;在您的实现中,这是一个float 正无穷大。

  1. 第二次扫描:为什么会这样舍入,我该如何解决?

浮点数具有固定大小的表示,就像所有 C 的内置数据类型一样。因此,它们的精度受到限制,并且在它们的范围内有许多数字无法准确表示。这是浮点数和整数之间的取舍:前一种类型的范围和规模更大,但它们通常不精确。

您的机器很可能使用 IEEE-754 二进制双精度作为 double 的格式。该格式提供 15-16 位十进制数字的精度。当某些输入转换为double(即使使用了不同的表示形式)时,您无法避免舍入,因为数字格式根本无法将输入表示为其原始精度。通过scanf() 读取输入而不进行四舍五入的唯一方法是将其读取为字符串而不是数字。

  1. 总的来说:我们可以看到,从 0 的数量来看,DBL_MAXFLT_MAX 比我给出的输入要大得多。那么为什么它不能正确存储输入呢?

除了我已经讨论过的有限精度问题,请注意您的第一个输入大于FLT_MAX,但您尝试将其读取为float。如果您认为输入在目标数字格式的范围内,那您就错了。

【讨论】:

  • 很好的信息。实际上,我本来希望 scanf() 在溢出的情况下失败;我发现the man page 在错误条件方面有所欠缺。对strtod 的引用仅与格式有关,与错误处理无关。例如,我可以期望 scanf() 设置errno吗?
  • @PeterA.Schneider,您引用的手册页明确指出,“如果发生读取错误,则设置流的错误指示符,返回 EOF,并设置 errno 以指示错误。”它还引用了fgetc()fgetwc() 以获取可能设置的错误,并指定了一些自己的附加错误。
  • @PeterA.Schneider,请注意,错误条件仅针对读取错误明确定义。 scanf() 不报告匹配失败的错误。此外,它不能报告超出范围的数字输入的匹配失败,因为在匹配失败的情况下,需要将整个未匹配的输入留在流上。您不能指望能够推回多个字符,因此当scanf() 可以检测到输入对应于超出范围的数字时,它会以某种方式进行转换。
  • 你说的都是正确的,但是通过将不同的信息位和背景知识结合在一起形成了一个连贯的画面(“最多可以推回一个字符”)。手册页可以(imo:应该)简单地列出数字溢出情况下的行为,或者单独为每个数字转换,或者,如果这太多余,在一个额外的段落中。在我看来,它是一个与其他错误类别不同的​​错误类别。我也不相信 scanf 一旦开始转换序列就会被提交——我想非法的字节序列会导致任何地方的中止。
  • @PeterA.Schneider:前提是至少读取了一位数字或.后跟一位数字,scanf致力于读取该数字,并且将读取最多但不包括不能构成数字一部分的第一个字符。因此“非法字节序列”将保留在输入流中,并且它们之前的数字将被消耗并返回。
【解决方案2】:
  1. 第一次扫描:为什么scanf 仍然说扫描结果为1,而您可以看到该值未正确存储?

第一个数字大于FLT_MAX,因此被正确解析为无穷大。

  1. 第二次扫描:为什么会这样舍入,我该如何解决 是吗?

第二个数字小于DBL_MAX,因此它为输入数字找到最佳的双精度浮点值。这就是显示的内容。如果你不明白这意味着什么, this article will help

  1. 总的来说:我们可以看到,通过 0 的数量,DBL_MAXFLT_MAX 比我给出的输入要大得多。那么为什么它不能正确存储输入呢?

不正确。您将较大的数字解析为双精度数,将较小的数字解析为浮点数。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-23
    • 1970-01-01
    相关资源
    最近更新 更多