Non- equivalency of mathematical expressions

Started by pointercrash(), July 31, 2010, 07:19:30 PM

Previous topic - Next topic

pointercrash()

Hi there,

just migrated a calendar program to C and had massive problems with the evaluation of expressions. In this example all vars are type of int.        datedays = datedays + (year >> 2) - ((3 * (year / 100 + 1)) >> 2) - ((int)(month * 0.4 + 2.3)); should be equivalent to     {
        datedays += year >> 2;
        datedays -= (3 * (year / 100 + 1)) >> 2;
        datedays -= (int)(month * 0.4 + 2.3);
    }
but it isn't. The first example always gives back 0 for datedays, the 3-liner works properly. I tried to cast partitial terms explicitely and switched off Optimization, but Pelles C fails with the single- liner. I tried the same example with OpenWatcom, GCC and VStudio - no problem at all.
Finally i wanted to get rid off floating point operations and came to               datedays = datedays + (year >> 2) - ((3 * (year / 100 + 1)) >> 2) - ((month * 4 + 23)/10);and this works with pelles c, too.

Looks as if there might be a bug with the evaluation of expressions containing float or double or what do You think?

frankie

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.
"It is better to be hated for what you are than to be loved for what you are not." - Andre Gide

TimoVJL

Test results:

PellesC v 5: 482 482

PellesC v 6: 0 482 no optimize
PellesC v 6: 5 482 max speed
PellesC v 6: 5 482 min size

Open Watcom C 32 v 2.0b1: 482 482

Digital Mars C v 8.52: 482 482

#include <stdio.h>

int main(int argc, char **argv)
{
        int datedays1, datedays2, year, month;

        datedays1 = datedays2 = 0;
        year = 2010;
        month = 8;
        datedays1 = datedays1 + (year >> 2) - ((3 * (year / 100 + 1)) >> 2) - ((int)(month * 0.4 + 2.3));
        {
                datedays2 += year >> 2;
                datedays2 -= (3 * (year / 100 + 1)) >> 2;
                datedays2 -= (int)(month * 0.4 + 2.3);
        }
#ifdef __POCC__
        printf("PellesC: %d %d\n", datedays1, datedays2);
#elif __WATCOMC__
        printf("Open Watcom C: %d %d\n", datedays1, datedays2);
#elif __DMC__
printf("Digital Mars C: %d %d\n", datedays1, datedays2);
#endif
        return 0;
}
May the source be with you

frankie

Find the WorkAround!
As Reported above the problem is related to the compiler registers allocator and/or inconsistencies between inlined and library versions of ___ftol (maybe the same problem also for ___ftoll).
As you can see better in the following piece of generated assembly code the compiler failes to correctly find the variables before float to long conversion:
_tst:
push ebp
...........
add eax,3
sar eax,2
neg eax
add eax,ebx
fld qword [ebp+(-8)]
call ___ftol
mov edx,eax
sub eax,edx      ;;<<=== This will always zero the result!!!
#line 18 "main.c"
#line 19 "main.c"
..?E_tst:
.........
#line 63 "main.c"
mov eax,487
fld qword [(@13)]
call ___ftol
mov edx,eax
mov ebx,eax
sub ebx,edx      ;;<<=== This will always zero the result!!!
............


The code line references are for the following test source (EDIT: It is a variation of Timo test program):
/****************************************************************************
*                                                                          *
* File    : main.c                                                         *
*                                                                          *
* Purpose : Console mode (command line) program.                           *
*                                                                          *
* History : Date      Reason                                               *
*           00/00/00  Created                                              *
*                                                                          *
****************************************************************************/

#include <stdio.h>

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

int tst1(int year, int month)
{
int datedays=0;
datedays = datedays + (year >> 2) - ((3 * (year / 100 + 1)) >> 2) - ((month * 4 + 23)/10);
return datedays;
}

/****************************************************************************
*                                                                          *
* Function: main                                                           *
*                                                                          *
* Purpose : Main entry point.                                              *
*                                                                          *
* History : Date      Reason                                               *
*           00/00/00  Created                                              *
*                                                                          *
****************************************************************************/

//int main(int argc, char *argv[])
//{
    //printf("Hello, world!\n");
//printf("test: %d, %d\n", tst(2009, 2), tst(2010,2));


    //return 0;
//}

#ifdef __POCC__
        #define Compiler "PellesC"
#elif __WATCOMC__
        #define Compiler "Open Watcom C"
#elif __DMC__
#define Compiler "Digital Mars C"
#endif

int main(int argc, char **argv)
{
        int datedays1, datedays2, year, month;

        datedays1 = datedays2 = 0;
        year = 2010;
        month = 8;
        datedays1 = datedays1 + (year >> 2) - ((3 * (year / 100 + 1)) >> 2) - ((int)(month * 0.4 + 2.3));
        {
                datedays2 += year >> 2;
                datedays2 -= (3 * (year / 100 + 1)) >> 2;
                datedays2 -= (int)(month * 0.4 + 2.3);
        }

        printf(Compiler ": %d %d %d %d\n", datedays1, datedays2, tst(year, month), tst1(year, month));

        return 0;
}


The Workaround simply consist to use the inline form of ___ftol (___ftoll), adding the pragma ftol(inlined):
/****************************************************************************
*                                                                          *
* File    : main.c                                                         *
*                                                                          *
* Purpose : Console mode (command line) program.                           *
*                                                                          *
* History : Date      Reason                                               *
*           00/00/00  Created                                              *
*                                                                          *
****************************************************************************/

#include <stdio.h>
#pragma ftol(inlined)


This Workaround should be effective for versions from 4.5 to actual 6.0

Anyway, for completeness, this is my compiler configuration:

  • RSRC0009.DLL: Version 6.00.0
  • SUPPORT.DLL: Version 6.00.0
  • CFORMAT.DLL: Version 6.00.1
  • PORC.DLL: Version 6.00.0
  • POBR.DLL: Version 6.00.1
  • SQLITE3.DLL: Version 3.6.12
  • POCC.EXE: Version 6.00.6
  • POASM.EXE: Version 6.00.4
  • POLINK.EXE: Version 6.00.1
  • IDESPAWN.EXE: Version 6.00.0
"It is better to be hated for what you are than to be loved for what you are not." - Andre Gide

pointercrash()

Quote from: frankie on August 06, 2010, 12:30:43 PM
Find the WorkAround!
...
The Workaround simply consist to use the inline form of ___ftol (___ftoll), adding the pragma ftol(inlined):
/****************************************************************************
*                                                                          *
* File    : main.c                                                         *
*                                                                          *
* Purpose : Console mode (command line) program.                           *
*                                                                          *
* History : Date      Reason                                               *
*           00/00/00  Created                                              *
*                                                                          *
****************************************************************************/

#include <stdio.h>
#pragma ftol(inlined)


This Workaround should be effective for versions from 4.5 to actual 6.0
Umm, that seems to work (at least for V6), thanks - but it's really an ugly bug and i'm wondering why it came up so late.

Those who are interested in the full calendar function including leap years and secular years, might take a look at The Everlasting Calendar. The modulo returns the day of the week starting with saturday as 0.