【问题标题】:C - Problem in finding sum of consecutive numbersC - 查找连续数字总和的问题
【发布时间】:2021-01-21 07:47:17
【问题描述】:
#include <stdio.h>
#include <string.h>

int main() {
    int start, end, i, sum = 0, temp, x, y, z;
    puts("Please enter the starting number: ");
    scanf("%d", &start);
    temp = start;
    x = start; y = start + 1;
    puts("Please enter the last number till you want to add: ");
    scanf("%d", &end);
    for (i = start; i < end; i++) {
        z = x + y;
        x = z;
        y = y + 1;
    }
    printf("The sum of the numbers starting from %d to %d is %d", temp, end, z);
}

谁能找出这段代码有什么问题?

对于较少的数字它可以正常工作,但是当我分配像 1 到 1000000 这样的大数字时,它不起作用,即它返回奇怪的值。

案例 1:

Please enter the starting number:
1

Please enter the last number till you want to add:
100

The sum of the numbers starting from 1 to 100 is 5050

但在这种情况下-

案例 2:

Please enter the starting number:
1


Please enter the last number till you want to add:
1000000


The sum of the numbers starting from 1 to 1000000 is 1784293664

这种情况有什么问题?提前致谢。

【问题讨论】:

  • 我觉得这个值对于int来说太长了,可能会溢出,尝试切换到unsigned long long
  • 注意:有符号整数溢出是未定义的行为。
  • @DavidRanieri 我很确定是UB,毕竟是规范中提到的直接例子。
  • @DavidRanieri Unsigned 整数溢出是在 C99 和 C89 中定义的行为。
  • @DavidRanieri 重申:无符号溢出在 C17、C11、C99、94、C89 中定义。不是implementation defined up to version C11。 C89 有:“涉及无符号操作数的计算永远不会溢出。因为不能由得到的无符号整数类型表示的结果以比得到的无符号整数类型可以表示的最大值大一的数字为模减少” C89 6.1.2.5.

标签: c loops for-loop


【解决方案1】:

您正在使用int,它的值范围只有-32,768 to 32,767-2,147,483,648 to 2,147,483,647,分别对应2 byte4 byte

您的代码应使用unsigned long long,其值范围为0 to 18,446,744,073,709,551,615

【讨论】:

    【解决方案2】:

    这是数据溢出的情况。

    对于从 1 到 1000000 的计算示例,从 1 到 x 的连续数字之和为 x(x+1)/2,即 500.000.500.000,该值大大高于 int 的范围 (2.147. 483 647)。

    考虑使用 long long int,不要忘记,无论变量的字节大小达到某个值,无论如何都会溢出。

    【讨论】:

      【解决方案3】:

      对于大数字,您必须使用unsigned long long int 而不是int。这里int 表示最多为 3 位或 4 位数字的整数,但是当您输入 5 位或更多位数字时,它会给出奇怪的值。

      【讨论】:

      • 嗨 Dakashayani,欢迎来到 StackOverflow。感谢您提供答案。我同意您的回答,但请注意 unsigned long long 字符不是基本的 C 数据类型,也许您的意思是 unsigned long long int。请相应地编辑帖子。
      【解决方案4】:

      求连续数之和的问题

      OP 的代码反复添加和尝试大于INT_MAX 的总和,导致结果不正确。

      1. 使用更宽的类型来计算总和,例如 long longintmax_t

      2. 性能:不要使用循环。整数[0...N]之和为N*(N+1)/2

      改为:

      //for (i = start; i < end; i++) {
      //    z = x + y;
      //    x = z;
      //    y = y + 1;
      //}
      
      // Sum 0 to start
      long long sum_start = ((long long) start + 1) * start / 2; // or (start + 1LL)*start/2;
      // Sum 0 to end
      long long sum_end   = ((long long) end + 1) * end / 2;
      
      //                                                       vvvv specifier change
      printf("The sum of the numbers starting from %d to %d is %lld", 
        temp, end, sum_end - (sum_start - start));
      

      【讨论】:

      • 你应该使用 sum of 0 to (start-1) 作为sum_start
      • @chqrlie 是的 - 形成计算 sum 的各种方法。 (我之前在我的/SO 一式四份的答案中确实已经解决了这个问题)。
      【解决方案5】:

      int 类型的范围不足以表示 startstop 的大值的总和值(例如,065536 的总和超过 2147483647)。变量xyz 应使用long long 类型定义,它保证表示的值最高可达9223372036854775807 (9.22E18) 或unsigned long 最高可达18446744073709551615 (1.84E19)。

      此外,计算总和的算法很复杂,其中包含不必要或未使用的中间变量。简单才是王道!

      这是修改后的版本:

      #include <stdio.h>
      #include <string.h>
      
      int main() {
          int start, end, i;
          long long sum;
      
          puts("Please enter the starting number: ");
          if (scanf("%d", &start) != 1)
              return 1;
      
          puts("Please enter the last number till you want to add: ");
          if (scanf("%d", &end) != 1)
              return 1;
      
          sum = 0;
          for (i = start; i <= end; i++) {
              sum = sum + i;
          }
          printf("The sum of the numbers from %d to %d is %lld\n", start, end, sum);
          return 0;
      }
      

      对于正的startstop 值,有一个直接的公式来计算结果。你可以用这个表达式替换 for 循环:

          if (start <= stop) {
              // take the sum of integers from 0 to stop: stop * (stop + 1) / 2
              sum = (long long)stop * (stop + 1) / 2;
              // subtract the sum of integers from 0 to start - 1: (start - 1) * start / 2
              sum -= (long long)start * (start - 1) / 2;
          } else {
              sum = 0;
          }
      

      简化为:

          if (start <= stop) {
              sum = ((long long)stop + start)(stop - start + 1) / 2;
          } else {
              sum = 0;
          }
      

      如果start 和/或stop 可以为负数,则总和仍可以直接计算为:

          if (start <= stop) {
              // take the sum of integers from 0 to stop - start:
              sum = (long long)(start - stop) * ((long long)stop - start + 1) / 2;
              // add the initial value (stop - start + 1) times
              sum += ((long long)stop - start + 1) * start;
          } else {
              sum = 0;
          }
      

      简化为:

          if (start <= stop) {
              sum = ((long long)stop + start)((long long)stop - start + 1) / 2;
          } else {
              sum = 0;
          }
      

      【讨论】:

      • @chux-ReinstateMonica:公式不适用于负的 startstop 值。他们巧合地为start == 0工作。
      • @chux-ReinstateMonica:实际上第二个公式也适用于负值 :)
      【解决方案6】:

      抱歉,您不知道以下计算总和的方法吗?

      long sum_from_a_to_b(long a, long b)
      {
          int n;
          int acc = 0;
          for (n = a; n <= b; n++)
              acc += n;
          return  acc;
      }
      

      顺便说一句,有一个更好的公式,不需要循环:

      #include <assert.h>
      /* assumed that a <= b */
      long sum_from_a_to_b(long a, long b)
      {
          assert(a <= b);
          return b*(b + 1)/2 - a*(a - 1)/2;
      }
      

      【讨论】:

      • 你能解释一下你的代码吗?我是 C 语言的初学者(其他人称 noob)。我无法理解你的代码。
      • @RaviKumar,代码的第一个 sn-p 只是针对ab 之间的每个整数(都包括在内)进行循环,只需将循环控制变量的值添加到 @ 987654326@蓄能器。第二个是应用已知公式得到等差数列总和的结果(查看at wikipedia,以获得彻底的解释)你必须付出一些努力才能得到它,我只向你展示了它的最终工作应用原则。
      • @RaviKumar,您遇到的问题是,对于 11000000,您最终将数字添加到最终结果 500000500000,这远远大于int 变量。这就是我在示例代码中包含long 类型值的原因,您的系统允许的最大int2147483647,比这小得多,并且int 变量溢出(返回到-2147483648 和给你可能的负面结果或完全不相关的数字)
      • 我理解“long”和“int”部分,但我不理解的是你的程序 - #include /* 假设 a
      • 只需执行man assert,您就可以访问该调用的文档。它只是检查参数是否为真,如果断言为假,则使用消息和调用位置中止程序。我把它写成一个谓词,在执行程序时必须始终为真。如果您不按顺序传递参数,例程将中止。
      【解决方案7】:

      在第二种情况下,您使用的数字大于整数数据类型的允许范围(对于 16 位架构,它从 -3276832767,对于 16 位架构,从 -2,147,483,6482,147,483,647 32 位架构。)这就是它导致integer overflow 问题的原因。要解决此问题,您可以使用其他更大的数据类型,例如 long intlong long int,但如果您不打算使用负数,我建议您使用它们的无符号数据类型,即 unsigned long intunsigned long long int,因为无符号数据类型的范围从 0 开始。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-11-01
        • 1970-01-01
        • 2015-07-27
        • 2017-05-30
        • 1970-01-01
        • 2016-03-24
        • 2021-12-25
        • 2022-12-16
        相关资源
        最近更新 更多