Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Operator * and + produces wrong result in digital mars

Tags:

c

I am trying to calculate a number which produce Longest Collatz sequence. But here is a strange problem. 3n+1 become 38654705674 when n is 3. I do not see an error. here is the full code:

/* 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;
}
like image 627
silentboy Avatar asked Dec 06 '25 17:12

silentboy


1 Answers

It's seems to be bug in dmc compiler, that fails to handle long long type correctly. Here is narrowed test-case:

#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;
}

It produces (little endian, so 0x900000009 == 38 654 705 673):

9       0       0       0       9       0       0       0

From dissasembly it looks that it stores 64-bit integer as two 32-bit registers:

.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

I additionaly tested it with objconv tool, that just confirms my initial diagnose:

#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;
}

disassembly (single section):

>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

Note that mul edx operates implicitely on eax. The result is stored in both registers, higher part (in this case 0) in stored in edx, while lower in eax.

like image 160
Grzegorz Szpetkowski Avatar answered Dec 08 '25 08:12

Grzegorz Szpetkowski



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!