Kleines Rätsel: Warum ist das nicht äquivalent?



  • Hallo Forumsbesucher,

    ich habe meinen ewigen Kalender jetzt auf C portiert und bin über ein Problem gestolpert. Ein Teil der Formel lautet:

    datedays = datedays + (year >> 2) - ((3 * (year / 100 + 1)) >> 2) - ((int)((double)month * 0.4 + 2.3));
    

    Den Wert für datedays plättet mir das Programm immer auf 0. In einer einzelnen Zeile ist schlecht debuggen, dewegen habe ich das Ding umgeformt:

    {
    		datedays += year >> 2;
    		datedays -= (3 * (year / 100 + 1)) >> 2;
    		datedays -= (int)((double)month * 0.4 + 2.3);
    	}
    

    Jetzt kommt der Gimmik: Es gibt nichts zu debuggen, das Ding tut - Teiloperation für Teiloperation kein Bug! 🤡
    In einer Zeile zusammengefasst zuverlässiger Nullgenerator, aufgeteilt ein Kalenderrechner - wo muß ich mich dafür bedanken? Ist das etwa nicht äquivalent oder baut der Compiler Blödsinn?
    Ich hab' schon versucht, den Einzeiler zu Disassemblieren, aber da verlier' ich mich in den Aufrufen, keine Ahnung, was das Ding da tut.
    Ist jemandem von Euch vielleicht klar, woher das total unterschiedliche Verhalten herrühren könnte? Ungeklärte Ursachen machen mich immer nervös ...

    Danke für's Angucken! 😃



  • Von welchem Typ sind year,month und datedays?



  • Hallo,

    ich sehe da auch keinen Unterschied. Ich tippe mal auf einen Compilerfehler. Welchen Compiler benutzt Du denn? Prüfe auch mal ob mit oder ohne Optimierung das gleiche Problem auftaucht.

    mfg Martin


  • Mod

    edit: Doch nicht.



  • Visual Studio macht's jedenfalls. 🙂



  • knivil schrieb:

    Von welchem Typ sind year,month und datedays?

    unsigned int.

    mgaeckler schrieb:

    Welchen Compiler benutzt Du denn? Prüfe auch mal ob mit oder ohne Optimierung das gleiche Problem auftaucht.

    Ein LCC- Derivat, Pelles C. Optimierung ist schon aus.

    Ad aCTa schrieb:

    Visual Studio macht's jedenfalls. 🙂

    Ja, ich hätte schon andere Compiler auch probiert, nur hab' ich die auf virtuellen Maschinen installiert und mein VMWare Server mag momentan nicht mehr starten. 😞

    Wer die ganze Funktion mal probieren möchte:

    int DateDays(unsigned int year, unsigned int month, unsigned int day)
    {
    	int datedays;
    	char *theday;
    
    		datedays = 365 * year + day + 31 * (month - 1); 
    
    	if (month < 3)
    	{
    		datedays += (year-1) >> 2; 
    		datedays -= (3 * (((year - 1)/100 + 1))) >> 2;
    	}
    	else
    	{
    		datedays += year >> 2;
    		datedays -= (3 * (year / 100 + 1)) >> 2;
    		datedays -= (int)((double)month * 0.4 + 2.3);
    	}
    
    //		datedays = datedays + (year >> 2) - ((3 * (year / 100 + 1)) >> 2) - ((int)((double)month * 0.4 + 2.3));
    
    	Day2Txt(datedays%7, &theday);
    	printf("%d days since 01/01/1582\n%s \n",datedays, theday);
    	return datedays;	
    }
    

    Ich hab' halt grad ein anderes Projekt mit Pelles C am Laufen, in dem verdammich viel gerechnet wird und da macht sowas ein ganz schlechtes Bauchgefühl ... 👎



  • Läuft mit g++ 4.4.1 als Einzeiler und Dreizeiler, mit oder ohne Optimierungen.
    Sicher, dass kein Fehler in Day2Txt vorliegt? Das Aufspalten der Codezeile sollte zwar nichts an der Adresse von datedays verändern, aber erfahrungsgemäß sollte man bei undefinierten Verhalten das Wort "eigentlich" gar nicht erst in den Mund nehmen.



  • Athar schrieb:

    Sicher, dass kein Fehler in Day2Txt vorliegt? Das Aufspalten der Codezeile sollte zwar nichts an der Adresse von datedays verändern, aber erfahrungsgemäß sollte man bei undefinierten Verhalten das Wort "eigentlich" gar nicht erst in den Mund nehmen.

    Um Dich zu beruhigen, die Zeile kann man auskommentieren, ohne daß sich was ändern würde. Day2Txt deponiert nur die Adresse auf nen kurzen Wochentagsstring in theday. Könnte man auch über ein Array machen.
    Hab's jetzt mal "as it works" gepostet. Hab' jetzt noch eine einzelne Kiste mit einem GCC gefunden (Uralt Kaputto- Laptop) und such' noch 'ne Diskette, die noch nicht kaputt ist, um mir das erneute Eintippen zu sparen ... 😉

    Aber ihr seid wohl auch eher der Meinung, daß da was am Compiler faul sein müßte, oder ?



  • pointercrash() schrieb:

    Aber ihr seid wohl auch eher der Meinung, daß da was am Compiler faul sein müßte, oder ?

    Ich sage mal, mit an Sicherheit grenzender Wahrscheinlichkeit. Habe das auch mit BC++ 5.02 ausprobiert, ohne Optimizer, mit Speed Optimizer und mit Size Optimizer. Ich bekomme jedesmal das gleicher Ergebnis.

    mfg Martin



  • Probier mal die Teilausdrücke im Einzeiler hart auf unsigned zu casten. Vielleicht geht was mit irgendwelchen integer promotions schief?

    Den double-Cast kannst du dir im übrigen schenken



  • Tim schrieb:

    Probier mal die Teilausdrücke im Einzeiler hart auf unsigned zu casten. Vielleicht geht was mit irgendwelchen integer promotions schief?

    Ich hatte ja schon einige Zeit wüst herumgecastet, auch mal alles auf signed umgestellt - keine Änderung.

    Mittlerweile habe ich auch meinen VMWare Server wieder zur Zusammenarbeit bewegen können und den Watcom, das VS2003 und 'nen Renesas drübergelassen, alle haben kein Problem damit, der Code funzt so oder so.

    Tim schrieb:

    Den double-Cast kannst du dir im übrigen schenken

    Richtig, der ist beim Verzweiflungsprobieren reingerutscht. Ich hab' dann mal Teilausdrücke rausgeschmissen und tatsächlich war dann

    ((int)(month * 0.4 + 2.3))
    

    der böse, böse Nullmacher. Wenn man nochmal hinguckt, sieht man, daß man sich den ganzen double- Kram sparen kann, dann sieht die Zeile so aus:

    datedays = datedays + (year >> 2) - ((3 * (year / 100 + 1)) >> 2) - ((month * 4 + 23)/10);
    

    ... und funktioniert auf einmal 👍 , egal, welcher Compiler, egal welche Optimierung.

    Das ist zwar einerseits schön, ich hab' ja nur Stunden darauf verschwendet 😡 , andererseits vermisse ich die Lehre, die ich daraus ziehen könnte. Falls jmd. dazu noch was einfällt, bitte, gerne!



  • hi pointercrash,

    Die Lehre ?
    + Nimm keine doubles wenn es sich vermeiden lässt
    + pack nicht soo viel in eine Zeile, wenn es nicht nötig ist

    zu guter letzt:
    Standards sind Standards
    Compiler sind Compiler und
    Klammern sind Klammern

    Gruß Frank



  • Danke, ich weiß dann schonmal welchen Compiler ich nicht verwenden werde. 😃



  • ... Compiler ... blabla ... Compiler ... blabla ... Compiler ... blabla

    unsigned int DateDays(unsigned int year, unsigned int month, unsigned int day)
    {
        unsigned int datedays = (365u * year) + day + (31u * (month - 1u));
        const char* theday = NULL;
    
        if (month < 3u)
        {
            datedays += (year - 1u) / 4u;
            datedays -= (3u * ((((year - 1u) / 100u) + 1u))) / 4u;
        }
        else
        {
            datedays += (year / 4u);
            datedays -= (3u * ((year / 100u) + 1u)) / 4u;
            datedays -= ((month * 4u) + 23u) / 10u;
        }
    
        datedays %= 7u;
    
        Day2Txt(datedays, &theday);
    
        if (NULL == theday)
        {
            printf("ERROR: Impossible case occured. (%s, %u)\n", __FUNCTION__, __LINE__);
            return 0;
        }
    
        printf("%u days since 01/01/1582\n%s \n", datedays, theday);
    
        return datedays;
    }
    

    Läuft garantiert mit jedem Compiler, würde aber meine Hand dafür nicht ins Feuer legen... 🙂



  • Hi Leute,

    der Fehler hat's in die offizielle Buglist geschafft.

    frankie schrieb:

    There is definitely a bug in the registers allocator. the last two instructions of the routine are always:

    mov edx,eax
        sub eax,edx
    

    Which result is obviously always zero.
    This is due to missing save of intermediary result before the float conversion, and the wrong assumption that it is still in EAX while the last register holds the conversion instead.

    Einen Workaround gibt's auch, indem man per #pragma auf die inlined- Version umschaltet.
    Bin gespannt, wann das regulär behoben wird.

    @abc.w:
    Sicher? Mit Zeile 18 machst Du datedays ja kaputt, dann bist Du mit jedem Compiler in der Zeitschleife und mußt für immer die erste Woche des Jahres 1582 wiederholen. 😉



  • pointercrash() schrieb:

    @abc.w: Sicher? Mit Zeile 18 machst Du datedays ja kaputt, ...

    Ach so, stimmt... wie heisst es so schön: "don't touch the running system" 🙂


Anmelden zum Antworten