【问题标题】:Facing problem with finding cube of large numbers面临寻找大数立方体的问题
【发布时间】:2019-10-31 06:02:58
【问题描述】:

我有这 20 个号码

409,29,657,523,1481,447,904,312,110,1207,55,284,65,232,102,206,218,565,731,34 

他们是群众,我需要找到 $m^{3}$ 的平均值。但它给了我错误的答案。当我在某个限制后添加它们时,总和会下降。所以我试图在每个立方体添加为mmmsum = mmmsum +m*m*m;后打印总和@

首先我尝试使用 int 数据类型 (%d) 并观察到总和下降,然后假设这可能是由于范围的限制我尝试了 unsigned int 然后我也遇到了同样的问题,所以我尝试了 unsigned long int 同样的问题,但只有更高的总和,然后我尝试了 unsigned long long,这一次总和比预期的要多,在两次总和后它再次下降

    int main()
    {
            int m[] ={409,29,657,523,1481,447,904,312,110,1207,55,284,65,232,102,206,218,565,731,34};
            int mmmsum=0;

            for(int i=0;i<20;i++)
            {
                mmmsum = mmmsum + m[i]*m[i]*m[i];
                printf("\n %d",mmmsum);
            }

            return 0;
    }

现在,如果我使用 pow 函数来计算多维数据集和 unsigned long int (%lu) 数据类型,它就会正确。为什么它不适用于 m*m*m

上面显示的代码是我为解码编写的代码:我正在发布一段原始代码,实际上数组 a 在实际问题中并不存在

    unsigned long long m_av,mm_av,mmm_av;
    ........
    //======= for M, MM, MMM =============
    m_av=0;mm_av=0;mmm_av=0;
    cluster = head;
    while(cluster)
    {
        l = cluster->label;
        m = L2[l];
        m_av    = m_av  + m;
        mm_av   = mm_av + m*m;
        mmm_av  = mmm_av+ pow(m,3);

        cluster = cluster->next_L;
    }
    m_av = m_av/Tnc;
    mm_av = mm_av/Tnc;
    mmm_av = mmm_av/Tnc;

    //===================================
fprintf(fp,"%lu\t%lf\t%d\t%llu\t%llu\t%llu etc.....\n",t_step,E,Tnc,m_av,mm_av,mmm_av,m_max,etc.....);

在一个总共有 100,000 个粒子的模拟中,我得到了如下代码

373926  0.225469    6   25000   48820678    109937352837987 . . .   
392623  0.225469    6   25000   48820678    109937352837987 . . .   
412254  0.205942    4   37500   194892454   179871291607140 . . .   
432867  0.205942    4   37500   194892454   179871291607140 . . .   
454510  0.205942    4   37500   194892454   179871291607140 . . . 
477235  0.020043    3   50000   6148914690883261936 664306051917360 . . 
501097  0.020043    3   50000   6148914690883261936 664306051917360 . .
526152  0.020043    3   50000   6148914690883261936 664306051917360 . .
552459  0.020043    3   50000   6148914690883261936 664306051917360 . .
580082  0.020043    3   50000   6148914690883261936 664306051917360 . .
609087  0.020043    3   50000   6148914690883261936 664306051917360 . .
639541  0.002030    2   75000   702942377   1247147318025000 . . .  
671518  0.002030    2   75000   702942377   1247147318025000 . . .  
705094  0.002030    2   75000   702942377   1247147318025000 . . .  
740348  0.002030    2   75000   702942377   1247147318025000 . . .

我认为这与这个问题有些关系-> C : Printing big numbers

【问题讨论】:

  • 您提供的代码中的m 是什么?
  • 您的 int 变量溢出。查看en.wikipedia.org/wiki/Integer_overflow的解释
  • 使用unsigned long 而不是int。格式说明符不是您唯一需要更改的内容。
  • 我认为int a[] = ... 应该是int m[] =...
  • 你为什么首先使用整数(任何大小)来表示像质量这样的物理量?

标签: c


【解决方案1】:

1481 * 1481 * 1481 = 3 248 367 641,不适合signed int(提供32位,不一定如此),最大值为2 147 483 647,所以会发生溢出(这是 signed 整数类型的未定义行为!)。

我尝试过 unsigned int、unsigned long 和 unsigned long long。

所有这些都应该足够大(对于 32 位,给出的范围高达 4 294 967 295 并包括在内)。实际上,&lt;stdint.h&gt;(例如uint64_t)中的类型更可取,因为它们具有保证的位宽。

无论如何,问题是:在哪里你试过了吗?将 array 的底层类型更改为这些更大的类型将立即消除问题(当然,前提是您也更改了 mmmsum 的类型)。

如果您想要或需要(无论出于何种原因)保留int 数组,那么您需要将您的操作数转换为更大的类型,否则m * m * m 仍将被计算为(太小)int。所以:

uint64_t mmmsum = 0;
// ...
    mmmsum += static_cast<uint64_t>(m[i]) * m[i] * m[i];

只转换第一个参数就足够了,因为其他参数将被隐式提升(转换)。

使用pow 做了非常相似的事情:由于函数参数是双精度类型,int 的值也被转换,在计算之前,双精度足以容纳题。但是,双精度计算可能会引入舍入误差,pow 即使您使用整数值。有时结果(最小)小于实际值,然后当转换回积分时,您会得到一个错误的值(太小了)。如果只使用整数值,如在给定的情况下,在回退之前添加 0.5 已经解决了问题......

关于有符号/无符号的旁注:不要决定某一位范围是有符号还是无符号——如果您需要更多范围,请切换到下一个更大的数据类型。关于使用有符号或无符号的决定只能由有意义或无意义的负值做出(显然在你的情况下是后者)。

【讨论】:

  • 我已经提到我尝试过 unsigned int、unsigned long 和 unsigned long long。请有人运行此代码,然后您就会明白。
  • @fahd 我明白了,不过显然你没有在正确的地方尝试——调整了答案......
  • @fahd 我现在要问的第一个问题:负质量有意义吗?如果没有,我会将 L2[l] 的类型更改为无符号(也许uint32_t 以获得保证的位宽,即使是质量本身?)。然后,如果你得到如此接近 32 位无符号的最大值,我会切换到 uint64_t 求和:uint64_t m_av = 0, mm_av = 0, mmm_av = 0;m 似乎只在循环内部相关,所以只在循环中声明它(始终保持变量范围尽可能本地)!。那么您将拥有:uint64_t m = L2[l]; 从现在开始,即使对于多维数据集,计算也是安全的:mmm_av += m * m * m;
  • 如果您坚持使用pow,我会推荐,然后更喜欢mmm_av += pow(m, 3) + 0.5; 以避免前面提到的舍入错误。
  • 问题是您实际上使用了int 常量,但对实现撒谎,告诉它 更大的类型(%llu -> unsigned long long)。现在发生的情况是堆栈上的int 的前四个(通常至少可以不同)字节将被使用,另外还有四个其他字节,将所有字节视为一个巨大的数字。但是,这四个字节可能包含任何垃圾,因此您可能会得到一些完全不相关的输出(仅在 LE 机器上,并且这四个字节偶然全部为零,您会得到想要的结果)。跨度>
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-09-09
  • 2022-09-24
  • 1970-01-01
  • 1970-01-01
  • 2011-08-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多