NO

Author Topic: Non- equivalency of mathematical expressions  (Read 4372 times)

pointercrash()

  • Guest
Non- equivalency of mathematical expressions
« on: July 31, 2010, 07:19:30 PM »
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.
Code: [Select]
        datedays = datedays + (year >> 2) - ((3 * (year / 100 + 1)) >> 2) - ((int)(month * 0.4 + 2.3)); should be equivalent to
Code: [Select]
    {
        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
Code: [Select]
              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?

Offline frankie

  • Global Moderator
  • Member
  • *****
  • Posts: 2113
Re: Non- equivalency of mathematical expressions
« Reply #1 on: August 05, 2010, 12:07:27 PM »
There is definitely a bug in the registers allocator. the last two instructions of the routine are always:
Code: [Select]
    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

Offline TimoVJL

  • Global Moderator
  • Member
  • *****
  • Posts: 2115
Re: Non- equivalency of mathematical expressions
« Reply #2 on: August 05, 2010, 02:07:28 PM »
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

Code: [Select]
#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

Offline frankie

  • Global Moderator
  • Member
  • *****
  • Posts: 2113
Re: Non- equivalency of mathematical expressions - Workaround Found
« Reply #3 on: August 06, 2010, 12:30:43 PM »
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:
Code: [Select]
_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):
Code: [Select]
/****************************************************************************
 *                                                                          *
 * 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):
Code: [Select]
/****************************************************************************
 *                                                                          *
 * 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
« Last Edit: August 08, 2010, 08:06:02 PM by frankie »
"It is better to be hated for what you are than to be loved for what you are not." - Andre Gide

pointercrash()

  • Guest
Re: Non- equivalency of mathematical expressions - Workaround Found
« Reply #4 on: August 10, 2010, 02:53:13 PM »
Find the WorkAround!
...
The Workaround simply consist to use the inline form of ___ftol (___ftoll), adding the pragma ftol(inlined):
Code: [Select]
/****************************************************************************
 *                                                                          *
 * 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.