运算符*和+在数字火星中产生错误的结果

sil*_*boy 3 c

我正在尝试计算产生最长Collat​​z序列的数字.但这是一个奇怪的问题.3n+1成为38654705674什么时候n3.我没有看到错误.这是完整的代码:

/* 6.c -- calculates Longest Collatz sequence */
#include <stdio.h>
long long get_collatz_length(long long);
int main(void)
{
    long long i;
    long long current, current_count, count;

    current_count = 1;
    current = 1;
    for(i=2;i<1000000;i++)
    {
        // works fine when i is 2 the next line take eternity when i is 3;
        count = get_collatz_length(i);
        if(current_count <= count)
        {
            current = i;
            current_count = count;
        }
    }
    printf("%lld %lld\n", current, current_count);

    return 0;
}

long long get_collatz_length(long long num)
{
    long long count;

    count = 1;
    while(num != 1)
    {
        printf("%lld\n", num);
        if(num%2)
        {
            num = num*3+1;        // here it is;
        }
        else
        {
            num/=2;
        }
        count++;
    }
    puts("");
    return count;
}
Run Code Online (Sandbox Code Playgroud)

Grz*_*ski 6

这似乎是dmc编译器中的错误,无法long long正确处理类型.这是缩小的测试用例:

#include <stdio.h>

int main(void)
{
    long long num = 3LL;

    /*printf("%lld\n", num);*/

    num = num * 3LL;

    char *t = (char *) &num;
    for (int i = 0; i < 8; i++)
        printf("%x\t", t[i]);
    putchar('\n');

    /*printf("%lld\n", num);*/

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

它产生(小端,所以0x900000009== 38 654 705 673):

9       0       0       0       9       0       0       0
Run Code Online (Sandbox Code Playgroud)

从dissasembly看,它将64位整数存储为两个32位寄存器:

.data:0x000000be    6bd203  imul edx,edx,0x3
.data:0x000000c1    6bc803  imul ecx,eax,0x3
.data:0x000000c4    03ca    add ecx,edx
.data:0x000000c6    ba03000000  mov edx,0x3
.data:0x000000cb    f7e2    mul edx
.data:0x000000cd    03d1    add edx,ecx
.data:0x000000cf    31c0    xor eax,eax
Run Code Online (Sandbox Code Playgroud)

我另外用objconv工具测试它,这证实了我的初步诊断:

#include <stdio.h>

void mul(void)
{
    long long a;
    long long c;

    a = 5LL;
    c = a * 3LL;

    printf("%llx\n", c);
}

int main(void)
{
    mul();

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

拆卸(单节):

>objconv.exe -fmasm ..\dm\bin\check.obj

_mul    PROC NEAR
        mov     eax, 5                                  ; 0000 _ B8, 00000005
        cdq                                             ; 0005 _ 99
        imul    edx, edx, 3                             ; 0006 _ 6B. D2, 03
        imul    ecx, eax, 3                             ; 0009 _ 6B. C8, 03
        add     ecx, edx                                ; 000C _ 03. CA
        mov     edx, 3                                  ; 000E _ BA, 00000003
        mul     edx                                     ; 0013 _ F7. E2
        add     edx, ecx                                ; 0015 _ 03. D1
        push    edx                                     ; 0017 _ 52
        push    eax                                     ; 0018 _ 50
        push    offset FLAT:?_001                       ; 0019 _ 68, 00000000(segrel)
        call    _printf                                 ; 001E _ E8, 00000000(rel)
        add     esp, 12                                 ; 0023 _ 83. C4, 0C
        ret                                             ; 0026 _ C3
_mul    ENDP
Run Code Online (Sandbox Code Playgroud)

请注意,mul edx隐含地操作eax.结果存储在两个寄存器中,较高的部分(在这种情况下0)存储在edx,而在较低的位置eax.

  • @silentboy:是的,但它有点偏执,`printf("%llx \n",num);`也显示正确的十六进制值. (2认同)
  • 现在对基本类型的操作有这样的错误是很奇怪的.但是我不知道为什么OP使用它,因为我之前从未听过这个编译器 (2认同)