【问题标题】:Imprecise average at run time运行时不精确的平均值
【发布时间】:2019-01-05 08:49:54
【问题描述】:

我正在尝试解决一个问题,但遇到了一些问题。
我必须找到一系列数字的运行平均值。
示例:

input 4 2 7 
output 4 3 4.3333

现在问题来了,虽然我得到了答案,但这不是准确的答案。

接受的输出:accuracy difference shown in the image

 290.6666666667
 385.4000000000
 487.8333333333
 477.4285714286
 496.4444444444
 ...
 523.8571166992
 506.0454406738
 495.3043518066

我找不到问题所在。一些帮助将不胜感激。

#include<stdio.h>

main(){
  int n;
  printf("set:");
  scanf("%d",&n);
  float arr[n+1],resarr[n+1];
  float sum=0;

  for(int i=1; i<=n; i++){
    scanf("%f",&arr[i]);
    sum=arr[i]+sum;

    float res= sum/(float)i;
    resarr[i]=res;
  }
  int i=1;
  while(i<=n) {
    printf("%0.10f\n",resarr[i]);
    i++;
  }

  return 0;
}

【问题讨论】:

  • 您似乎忘记了数组索引是从零开始的。 IE。 n 元素的数组,索引从 0n - 1(含)。
  • “最大运行时间”(您的标题)与您的问题有什么关系?
  • @Someprogrammerdude 我很抱歉完全是我的错,我的意思是运行时的平均值
  • 您知道float 的精度非常有限吗?如果你改用double 会发生什么?另外,请阅读Is floating point math broken?
  • 解决了,非常感谢!

标签: c average precision


【解决方案1】:

这里

for(int i=1; i<=n; i++){ }

您正在尝试访问超出范围的数组元素,这肯定会导致 未定义的行为,因为我们假设如果 n5 那么您正在访问的 arr[5] 也不存在.

C 不执行数组边界条件检查,它的程序员有责任不访问越界元素,否则会导致 UB。

C 中,数组索引从0 开始,而不是从1。所以最好从0 开始旋转循环到n。例如

for(int i=0; i<n; i++) { 
   scanf("%f",&arr[i]);
   /* some code */
}

【讨论】:

  • 我也在使用 i 进行除法,所以我没有将 0 迭代到 n ,而是将数组的大小更改为 n+1 但我的准确性问题仍然存在
  • @pfn 好吧,没有什么能阻止您使用更惯用的范围,然后将平均值计算为 double avg = (double)sum / (i + 1);
  • "you are trying to access outbound array elements," --> No. code is not access outside the array arr[i] as it is defined with arr[n+1].
  • @Bob__ Casting sum 是不够的,因为保存的运行总和还需要为 double sum 以确保 sum=arr[i]+sum; 的精度。
【解决方案2】:

由于使用float 而不是double,代码未能达到所需的准确度。 @Some programmer dude

典型的float 精确到 223 中的 1 个部分。要打印到0.0000000001,最好使用double,即typically precise 到253 的1 个部分。

#include<stdio.h>

int main(void) {
  //float arr[n + 1], resarr[n + 1];
  //float sum = 0;
  double arr[n + 1], resarr[n + 1];
  double sum = 0;
  ...

    // scanf("%f", &arr[i]);
    scanf("%lf", &arr[i]);

    ...
    // float res = sum / (float) i;
    double res = sum / i;  // cast not needed as `sum` is `double`
  ...
}

从 1 开始迭代在 C 中不是惯用的。更常见的是从 0 开始迭代。

size_t 最适合数组大小调整和索引。 int 可能太窄了。当然,对于小数组,它几乎没有区别。

#include<stdio.h>

int main(void) {
  printf("set:");
  size_t n;
  scanf("%zu", &n);
  double arr[n], resarr[n];
  double sum = 0;

  for (size_t i = 0; i < n; i++) {
    scanf("%lf", &arr[i]);
    sum = arr[i] + sum;

    double res = sum / (i+1);
    resarr[i] = res;
  }

  for (size_t i = 0; i < n; i++) {
    printf("%0.10f\n", resarr[i]);
  }

  return 0;
}

更健壮的代码会检查用户的输入以确保其有效,如果允许n 很大,则分配而不是使用 VLA,在读取之前刷新输出等。

请注意,数组arr[] 不是必需的,只需一个double 用于输入和sum

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-11-30
    • 1970-01-01
    • 1970-01-01
    • 2013-12-24
    • 2020-03-26
    • 2010-12-19
    • 1970-01-01
    相关资源
    最近更新 更多