Merge lp:~epics-core/epics-base/fix-calc-bit-manipulation into lp:~epics-core/epics-base/3.14

Proposed by Ralph Lange
Status: Merged
Approved by: Andrew Johnson
Approved revision: 12615
Merged at revision: 12611
Proposed branch: lp:~epics-core/epics-base/fix-calc-bit-manipulation
Merge into: lp:~epics-core/epics-base/3.14
Diff against target: 371 lines (+141/-53)
3 files modified
src/libCom/calc/calcPerform.c (+35/-22)
src/libCom/calc/postfix.c (+15/-12)
src/libCom/test/epicsCalcTest.cpp (+91/-19)
To merge this branch: bzr merge lp:~epics-core/epics-base/fix-calc-bit-manipulation
Reviewer Review Type Date Requested Status
Ralph Lange Approve
Andrew Johnson Approve
Review via email: mp+286515@code.launchpad.net

Description of the change

Fix for bug lp:1514520

- Pulls a backport of a 3.15 change to the calc engine, setting the internal integer format to the size-fixed epicsInt32 type.
- Adds tests that show the behavior described in the bug report.
- Applies the original patch by Dirk from that bug report.

To be able to apply this to 3.14, I had to backport the change to the sized-fixed integers. For sanity.

When applying to 3.15, that first commit has to be omitted.

One thing that I am not sure of: left shifts (zeroes filled in) are handled different than right shifts (ones filled in, assuming a signed integer operation).
The documentation (AppDevGuide) leaves it open.

To post a comment you must log in.
12614. By Andrew Johnson

Fixed conversion overflows in tests

Minor tidying-up, added comments about casting for bitwise operations.

Revision history for this message
Andrew Johnson (anj) wrote :

Added an epicsUInt32 utop variable to calcPerform() to reduce the number of casts, and added some code comments.

Previous versions have always handled shifts as signed quantities, so the sign bit is extended on right-shifts. I changed the left-shift code to match, although it doesn't actually make any difference.

The shift operators also need to have their shift count limited to avoid Undefined Behavior; I see 2 realistic possibilities: ANDing the shift count with 31; or return 0 on shift-overflow but when right-shifting a negative quantity return -1 on shift-overflow. The first is the fastest and simplest to code, but the second one might be regarded as be more logical. Any preferences or alternative suggestions? I have implemented the first option for now.

The large positive decimal values in your tests are causing the same overflows when converted from double to integer that Dirk originally reported, they always convert to 0x80000000 on my RHEL systems so some tests fail. The paragraph before Dirk's "The fix is:" line explains that you have to use negative values for bit 31 to be set, which my tests agree with. The negative versions that work are 0xaaaaaaaa = -1431655766, 0xffff0000 = -65536. Note that postfix() compiles double literals into a LITERAL_INT op-code if the value can be safely held in an epicsInt32, so adding ".0" has no effect; but using ".1" instead does work, the fraction gets truncated.

Also fixed some minor white-space issues.

review: Approve
Revision history for this message
Ralph Lange (ralph-lange) wrote :

> Added an epicsUInt32 utop variable to calcPerform() to reduce the number of
> casts, and added some code comments.

Looks good, thanks.

> Previous versions have always handled shifts as signed quantities, so the sign
> bit is extended on right-shifts.

Fine with me. I was just seeing the asymmetry.

> The shift operators also need to have their shift count limited to avoid
> Undefined Behavior; I see 2 realistic possibilities: ANDing the shift count
> with 31; or return 0 / -1 on shift-overflow [...]
> I have implemented the first option for now.

Fine with me. We don't have to fix the holes in the definition.

> The large positive decimal values in your tests are causing the same overflows
> when converted from double to integer that Dirk originally reported, they
> always convert to 0x80000000 on my RHEL systems so some tests fail.

Interesting. Is that a compiler or a size dependency? My Debian 64bit gcc5 machine was testing them fine.

Thanks a lot!

Revision history for this message
Andrew Johnson (anj) wrote :
Download full text (4.6 KiB)

> Is that a compiler or a size dependency? My Debian 64bit gcc5 machine was testing them fine.

Don't know, I was testing on RHEL 6.7 with both 64 and 32-bit builds. Let me boot my Ubuntu VM to see what that does...
No difference; I'm doing manual checks on an IOC with a single calc record loaded.

This is with an unfixed 3.14 IOC, 64-bit:
tux% caget -lx anj:calc.CALC anj:calc
anj:calc.CALC a:=2863311530.0; a >> 8
anj:calc 0xAAAAAA

This is a 3.15 IOC, 64-bit:
tux% caget -lx anj:calc.CALC anj:calc
anj:calc.CALC a:=2863311530.0; a >> 8
anj:calc 0xFFFFFFFFFF800000

Then with this branch, 64-bit:
tux% caget -lx anj:calc.CALC anj:calc
anj:calc.CALC a:=2863311530.0; a >> 8
anj:calc 0xFFFFFFFFFFAAAAAA

The difference between 3.14 and 3.15 is because the bit-wise expressions were changed from long to epicsInt32, which fixed the shift to make them sign-extend properly, but show the broken value conversions. The final result above is correct (the sign extension on the results above are being done by caget -lx on 64-bit). I can get the right result from 3.15 using the negative value instead:
tux% caget -lx anj:calc.CALC anj:calc
anj:calc.CALC a:=-1431655766.1; a >> 8
anj:calc 0xFFFFFFFFFFAAAAAA

I think I got a bit confused when I was working on this last week; the negative value is evidently not (and maybe shouldn't be) necessary with the proper fix, so maybe I didn't need to change those tests. However I have just rewritten the double tests to check that we do convert the arguments properly for every bit-wise operator instead of duplicating the earlier integer tests:

    // converting double values (add 0.1 to force as double)
    // 0xaaaaaaaa = -1431655766 or 2863311530u
    testUInt32Calc("-1431655766.1 OR 0", 0xaaaaaaaau);
    testUInt32Calc("2863311530.1 OR 0", 0xaaaaaaaau);
    testUInt32Calc("0 OR -1431655766.1", 0xaaaaaaaau);
    testUInt32Calc("0 OR 2863311530.1", 0xaaaaaaaau);
    testUInt32Calc("-1431655766.1 XOR 0", 0xaaaaaaaau);
    testUInt32Calc("2863311530.1 XOR 0", 0xaaaaaaaau);
    testUInt32Calc("0 XOR -1431655766.1", 0xaaaaaaaau);
    testUInt32Calc("0 XOR 2863311530.1", 0xaaaaaaaau);
    testUInt32Calc("-1431655766.1 AND 0xffffffff", 0xaaaaaaaau);
    testUInt32Calc("2863311530.1 AND 0xffffffff", 0xaaaaaaaau);
    testUInt32Calc("0xffffffff AND -1431655766.1", 0xaaaaaaaau);
    testUInt32Calc("0xffffffff AND 2863311530.1", 0xaaaaaaaau);
    testUInt32Calc("~ -1431655766.1", 0x55555555u);
    testUInt32Calc("~ 2863311530.1", 0x55555555u);
    testUInt32Calc("-1431655766.1 >> 0", 0xaaaaaaaau);
    testUInt32Calc("2863311530.1 >> 0", 0xaaaaaaaau);
    testUInt32Calc("-1431655766.1 >> 0.1", 0xaaaaaaaau);
    testUInt32Calc("2863311530.1 >> 0.1", 0xaaaaaaaau);
    testUInt32Calc("-1431655766.1 << 0", 0xaaaaaaaau);
    testUInt32Calc("2863311530.1 << 0", 0xaaaaaaaau);
    testUInt32Calc("-1431655766.1 << 0.1", 0xaaaaaaaau);
    testUInt32Calc("2863311530.1 << 0.1", 0xaaaaaaaau);

Hmm, I just copied the resulting epicsCalcTest.cpp file into an unmodified 3.14 branch, where it happily...

Read more...

review: Needs Information
Revision history for this message
Ralph Lange (ralph-lange) wrote :

That's a nice one, eh?! When working on it last week, I got knots in my brain.

First of all, I set a narrow scope: I decided that I wanted to fix the calc engine, not the calc(out) record. To achieve that, I based the main tests on setting fields to double values before doing the operation, as this gets as close as possible to what the calc record does.
The other tests (using literals and setting fields to integers) were added later when I saw different failure patterns between them and the main tests.

On plain 3.14, the operations completely depend on sizeof(long). On 64bit things work fine (as setting bit31 is *not* setting the sign bit), on 32bit things fail.

To get a more reproducible situation, I first backported the change in the calc engine that changes all operations to work on fixed 32bit size types. With that change, I had the tests consistently failing in a pattern that matched Dirk's report.

After applying Dirk's patch, all tests passed. That's the state I pushed to LaunchPad.

I never tested in a real IOC using Dirk's database.
Having a test producing the same failure pattern that Dirk saw using his database, and his fix passing all my tests and (as per Dirk's report) his database testing seemed good enough to me.

Revision history for this message
Ralph Lange (ralph-lange) wrote :

Note that your caget based tests add another conversion (CA server converting from the calc record's double VAL field to integer).

Too many chained conversions in one test, for my taste.

12615. By Andrew Johnson

Test each bitwise cast individually for overflow

Revision history for this message
Andrew Johnson (anj) wrote :

Ok, so I wasn't testing against 3.14 on 32-bit, my bad — the tests do indeed fail there, thanks for pointing that out.

I have committed my changes to the double value tests that I posted yesterday, they are designed to check each cast in the bit-wise operator implementation individually for not overflowing with either +ve or -ve double values that have bit 31 set.

I agree that my caget technique introduced another conversion, I was trying to allow for that in my understanding of what's actually going on. Your testUInt32Calc() routine does the same conversion though:
    uresult = (epicsUInt32) result;
We could avoid that completely by doing the test comparisons inside the expression being tested:
- testUInt32Calc("0xaaaaaaaa AND 0xffff0000", 0xaaaa0000u);
+ testCalc("(0xaaaaaaaa AND 0xffff0000) == 0xaaaa0000", 1);
However the returned result is less useful for diagnosing overflow problems, so I'm happy to keep what we have now.

If you're happy with this I will merge it.

review: Approve
Revision history for this message
Ralph Lange (ralph-lange) wrote :

I am happy with it!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/libCom/calc/calcPerform.c'
--- src/libCom/calc/calcPerform.c 2010-10-07 19:16:24 +0000
+++ src/libCom/calc/calcPerform.c 2016-02-23 18:42:17 +0000
@@ -21,6 +21,7 @@
21#include "osiUnistd.h"21#include "osiUnistd.h"
22#include "dbDefs.h"22#include "dbDefs.h"
23#include "epicsMath.h"23#include "epicsMath.h"
24#include "epicsTypes.h"
24#include "errlog.h"25#include "errlog.h"
25#include "postfix.h"26#include "postfix.h"
26#include "postfixPvt.h"27#include "postfixPvt.h"
@@ -43,7 +44,8 @@
43 double stack[CALCPERFORM_STACK+1]; /* zero'th entry not used */44 double stack[CALCPERFORM_STACK+1]; /* zero'th entry not used */
44 double *ptop; /* stack pointer */45 double *ptop; /* stack pointer */
45 double top; /* value from top of stack */46 double top; /* value from top of stack */
46 int itop; /* integer from top of stack */47 epicsInt32 itop; /* integer from top of stack */
48 epicsUInt32 utop; /* unsigned integer from top of stack */
47 int op;49 int op;
48 int nargs;50 int nargs;
4951
@@ -55,14 +57,14 @@
55 switch (op){57 switch (op){
5658
57 case LITERAL_DOUBLE:59 case LITERAL_DOUBLE:
58 memcpy((void *)++ptop, pinst, sizeof(double));60 memcpy(++ptop, pinst, sizeof(double));
59 pinst += sizeof(double);61 pinst += sizeof(double);
60 break;62 break;
6163
62 case LITERAL_INT:64 case LITERAL_INT:
63 memcpy(&itop, pinst, sizeof(int));65 memcpy(&itop, pinst, sizeof(epicsInt32));
64 *++ptop = itop;66 *++ptop = itop;
65 pinst += sizeof(int);67 pinst += sizeof(epicsInt32);
66 break;68 break;
6769
68 case FETCH_VAL:70 case FETCH_VAL:
@@ -136,11 +138,11 @@
136 break;138 break;
137139
138 case MODULO:140 case MODULO:
139 itop = (long) *ptop--;141 itop = (epicsInt32) *ptop--;
140 if (itop)142 if (itop)
141 *ptop = (long) *ptop % itop;143 *ptop = (epicsInt32) *ptop % itop;
142 else144 else
143 *ptop = epicsNAN; /* NaN */145 *ptop = epicsNAN;
144 break;146 break;
145147
146 case POWER:148 case POWER:
@@ -261,7 +263,7 @@
261263
262 case NINT:264 case NINT:
263 top = *ptop;265 top = *ptop;
264 *ptop = (double)(long)(top >= 0 ? top + 0.5 : top - 0.5);266 *ptop = (epicsInt32) (top >= 0 ? top + 0.5 : top - 0.5);
265 break;267 break;
266268
267 case RANDOM:269 case RANDOM:
@@ -282,34 +284,45 @@
282 *ptop = ! *ptop;284 *ptop = ! *ptop;
283 break;285 break;
284286
287 /* For bitwise operations on values with bit 31 set, double values
288 * must first be cast to unsigned to correctly set that bit; the
289 * double value must be negative in that case. The result must be
290 * cast to a signed integer before converting to the double result.
291 */
292
285 case BIT_OR:293 case BIT_OR:
286 itop = (long) *ptop--;294 utop = *ptop--;
287 *ptop = (long) *ptop | itop;295 *ptop = (epicsInt32) ((epicsUInt32) *ptop | utop);
288 break;296 break;
289297
290 case BIT_AND:298 case BIT_AND:
291 itop = (long) *ptop--;299 utop = *ptop--;
292 *ptop = (long) *ptop & itop;300 *ptop = (epicsInt32) ((epicsUInt32) *ptop & utop);
293 break;301 break;
294302
295 case BIT_EXCL_OR:303 case BIT_EXCL_OR:
296 itop = (long) *ptop--;304 utop = *ptop--;
297 *ptop = (long) *ptop ^ itop;305 *ptop = (epicsInt32) ((epicsUInt32) *ptop ^ utop);
298 break;306 break;
299307
300 case BIT_NOT:308 case BIT_NOT:
301 itop = (long) *ptop;309 utop = *ptop;
302 *ptop = ~itop;310 *ptop = (epicsInt32) ~utop;
303 break;311 break;
304312
313 /* The shift operators use signed integers, so a right-shift will
314 * extend the sign bit into the left-hand end of the value. The
315 * double-casting through unsigned here is important, see above.
316 */
317
305 case RIGHT_SHIFT:318 case RIGHT_SHIFT:
306 itop = (long) *ptop--;319 utop = *ptop--;
307 *ptop = (long) *ptop >> itop;320 *ptop = ((epicsInt32) (epicsUInt32) *ptop) >> (utop & 31);
308 break;321 break;
309322
310 case LEFT_SHIFT:323 case LEFT_SHIFT:
311 itop = (long) *ptop--;324 utop = *ptop--;
312 *ptop = (long) *ptop << itop;325 *ptop = ((epicsInt32) (epicsUInt32) *ptop) << (utop & 31);
313 break;326 break;
314327
315 case NOT_EQ:328 case NOT_EQ:
@@ -381,7 +394,7 @@
381 pinst += sizeof(double);394 pinst += sizeof(double);
382 break;395 break;
383 case LITERAL_INT:396 case LITERAL_INT:
384 pinst += sizeof(int);397 pinst += sizeof(epicsInt32);
385 break;398 break;
386 case MIN:399 case MIN:
387 case MAX:400 case MAX:
@@ -468,7 +481,7 @@
468 pinst += sizeof(double);481 pinst += sizeof(double);
469 break;482 break;
470 case LITERAL_INT:483 case LITERAL_INT:
471 pinst += sizeof(int);484 pinst += sizeof(epicsInt32);
472 break;485 break;
473 case MIN:486 case MIN:
474 case MAX:487 case MAX:
475488
=== modified file 'src/libCom/calc/postfix.c'
--- src/libCom/calc/postfix.c 2013-11-19 21:26:22 +0000
+++ src/libCom/calc/postfix.c 2016-02-23 18:42:17 +0000
@@ -22,6 +22,7 @@
22#include "dbDefs.h"22#include "dbDefs.h"
23#include "epicsStdlib.h"23#include "epicsStdlib.h"
24#include "epicsString.h"24#include "epicsString.h"
25#include "epicsTypes.h"
25#include "postfix.h"26#include "postfix.h"
26#include "postfixPvt.h"27#include "postfixPvt.h"
27#include "shareLib.h"28#include "shareLib.h"
@@ -216,7 +217,7 @@
216 char * const pdest = pout;217 char * const pdest = pout;
217 char *pnext;218 char *pnext;
218 double lit_d;219 double lit_d;
219 int lit_i;220 epicsInt32 lit_i;
220221
221 if (psrc == NULL || *psrc == '\0' ||222 if (psrc == NULL || *psrc == '\0' ||
222 pout == NULL || perror == NULL) {223 pout == NULL || perror == NULL) {
@@ -249,27 +250,29 @@
249 goto bad;250 goto bad;
250 }251 }
251 psrc = pnext;252 psrc = pnext;
252 lit_i = (int) lit_d;253 lit_i = (epicsInt32) lit_d;
253 if (lit_d != (double) lit_i) {254 if (lit_d != (double) lit_i) {
254 *pout++ = pel->code;255 *pout++ = pel->code;
255 memcpy(pout, (void *)&lit_d, sizeof(double));256 memcpy(pout, &lit_d, sizeof(double));
256 pout += sizeof(double);257 pout += sizeof(double);
257 } else {258 } else {
258 *pout++ = LITERAL_INT;259 *pout++ = LITERAL_INT;
259 memcpy(pout, (void *)&lit_i, sizeof(int));260 memcpy(pout, &lit_i, sizeof(epicsInt32));
260 pout += sizeof(int);261 pout += sizeof(epicsInt32);
261 }262 }
262 }263 }
263 else {264 else {
264 lit_i = strtoul(psrc, &pnext, 0);265 epicsUInt32 lit_ui;
266
267 lit_ui = (epicsUInt32) strtoul(psrc, &pnext, 0);
265 if (pnext == psrc) {268 if (pnext == psrc) {
266 *perror = CALC_ERR_BAD_LITERAL;269 *perror = CALC_ERR_BAD_LITERAL;
267 goto bad;270 goto bad;
268 }271 }
269 psrc = pnext;272 psrc = pnext;
270 *pout++ = LITERAL_INT;273 *pout++ = LITERAL_INT;
271 memcpy(pout, (void *)&lit_i, sizeof(int));274 memcpy(pout, &lit_ui, sizeof(epicsUInt32));
272 pout += sizeof(int);275 pout += sizeof(epicsUInt32);
273 }276 }
274277
275 operand_needed = FALSE;278 operand_needed = FALSE;
@@ -594,18 +597,18 @@
594 };597 };
595 char op;598 char op;
596 double lit_d;599 double lit_d;
597 int lit_i;600 epicsInt32 lit_i;
598 601
599 while ((op = *pinst) != END_EXPRESSION) {602 while ((op = *pinst) != END_EXPRESSION) {
600 switch (op) {603 switch (op) {
601 case LITERAL_DOUBLE:604 case LITERAL_DOUBLE:
602 memcpy((void *)&lit_d, ++pinst, sizeof(double));605 memcpy(&lit_d, ++pinst, sizeof(double));
603 printf("\tDouble %g\n", lit_d);606 printf("\tDouble %g\n", lit_d);
604 pinst += sizeof(double);607 pinst += sizeof(double);
605 break;608 break;
606 case LITERAL_INT:609 case LITERAL_INT:
607 memcpy((void *)&lit_i, ++pinst, sizeof(int));610 memcpy(&lit_i, ++pinst, sizeof(epicsInt32));
608 printf("\tInteger %d\n", lit_i);611 printf("\tInteger %d (0x%x)\n", lit_i, lit_i);
609 pinst += sizeof(int);612 pinst += sizeof(int);
610 break;613 break;
611 case MIN:614 case MIN:
612615
=== modified file 'src/libCom/test/epicsCalcTest.cpp'
--- src/libCom/test/epicsCalcTest.cpp 2015-02-17 22:21:13 +0000
+++ src/libCom/test/epicsCalcTest.cpp 2016-02-23 18:42:17 +0000
@@ -8,6 +8,7 @@
8// Author: Andrew Johnson8// Author: Andrew Johnson
99
10#include "epicsUnitTest.h"10#include "epicsUnitTest.h"
11#include "epicsTypes.h"
11#include "epicsMath.h"12#include "epicsMath.h"
12#include "epicsAlgorithm.h"13#include "epicsAlgorithm.h"
13#include "postfix.h"14#include "postfix.h"
@@ -38,32 +39,59 @@
38 /* Evaluate expression, test against expected result */39 /* Evaluate expression, test against expected result */
39 bool pass = false;40 bool pass = false;
40 double args[CALCPERFORM_NARGS] = {41 double args[CALCPERFORM_NARGS] = {
41 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.042 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0
42 };43 };
43 char rpn[MAX_POSTFIX_SIZE];44 char rpn[MAX_POSTFIX_SIZE];
44 short err;45 short err;
45 double result = 0.0;46 double result = 0.0;
46 result /= result; /* Start as NaN */47 result /= result; /* Start as NaN */
47 48
48 if (postfix(expr, rpn, &err)) {49 if (postfix(expr, rpn, &err)) {
49 testDiag("postfix: %s in expression '%s'", calcErrorStr(err), expr);50 testDiag("postfix: %s in expression '%s'", calcErrorStr(err), expr);
50 } else51 } else
51 if (calcPerform(args, &result, rpn) && finite(result)) {52 if (calcPerform(args, &result, rpn) && finite(result)) {
52 testDiag("calcPerform: error evaluating '%s'", expr);53 testDiag("calcPerform: error evaluating '%s'", expr);
53 }54 }
54 55
55 if (finite(expected) && finite(result)) {56 if (finite(expected) && finite(result)) {
56 pass = fabs(expected - result) < 1e-8;57 pass = fabs(expected - result) < 1e-8;
57 } else if (isnan(expected)) {58 } else if (isnan(expected)) {
58 pass = (bool) isnan(result);59 pass = (bool) isnan(result);
59 } else {60 } else {
60 pass = (result == expected);61 pass = (result == expected);
61 }62 }
62 if (!testOk(pass, "%s", expr)) {63 if (!testOk(pass, "%s", expr)) {
63 testDiag("Expected result is %g, actually got %g", expected, result);64 testDiag("Expected result is %g, actually got %g", expected, result);
64 calcExprDump(rpn);65 calcExprDump(rpn);
65 }66 }
66 return;67}
68
69void testUInt32Calc(const char *expr, epicsUInt32 expected) {
70 /* Evaluate expression, test against expected result */
71 bool pass = false;
72 double args[CALCPERFORM_NARGS] = {
73 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0
74 };
75 char rpn[MAX_POSTFIX_SIZE];
76 short err;
77 epicsUInt32 uresult;
78 double result = 0.0;
79 result /= result; /* Start as NaN */
80
81 if (postfix(expr, rpn, &err)) {
82 testDiag("postfix: %s in expression '%s'", calcErrorStr(err), expr);
83 } else
84 if (calcPerform(args, &result, rpn) && finite(result)) {
85 testDiag("calcPerform: error evaluating '%s'", expr);
86 }
87
88 uresult = (epicsUInt32) result;
89 pass = (uresult == expected);
90 if (!testOk(pass, "%s", expr)) {
91 testDiag("Expected result is 0x%x (%u), actually got 0x%x (%u)",
92 expected, expected, uresult, uresult);
93 calcExprDump(rpn);
94 }
67}95}
6896
69void testArgs(const char *expr, unsigned long einp, unsigned long eout) {97void testArgs(const char *expr, unsigned long einp, unsigned long eout) {
@@ -238,8 +266,8 @@
238 const double a=1.0, b=2.0, c=3.0, d=4.0, e=5.0, f=6.0,266 const double a=1.0, b=2.0, c=3.0, d=4.0, e=5.0, f=6.0,
239 g=7.0, h=8.0, i=9.0, j=10.0, k=11.0, l=12.0;267 g=7.0, h=8.0, i=9.0, j=10.0, k=11.0, l=12.0;
240 268
241 testPlan(577);269 testPlan(613);
242 270
243 /* LITERAL_OPERAND elements */271 /* LITERAL_OPERAND elements */
244 testExpr(0);272 testExpr(0);
245 testExpr(1);273 testExpr(1);
@@ -883,7 +911,51 @@
883 testBadExpr("1?", CALC_ERR_CONDITIONAL);911 testBadExpr("1?", CALC_ERR_CONDITIONAL);
884 testBadExpr("1?1", CALC_ERR_CONDITIONAL);912 testBadExpr("1?1", CALC_ERR_CONDITIONAL);
885 testBadExpr(":1", CALC_ERR_SYNTAX);913 testBadExpr(":1", CALC_ERR_SYNTAX);
886 914
915 // Bit manipulations wrt bit 31 (bug lp:1514520)
916 // using integer literals
917 testUInt32Calc("0xaaaaaaaa AND 0xffff0000", 0xaaaa0000u);
918 testUInt32Calc("0xaaaaaaaa OR 0xffff0000", 0xffffaaaau);
919 testUInt32Calc("0xaaaaaaaa XOR 0xffff0000", 0x5555aaaau);
920 testUInt32Calc("~0xaaaaaaaa", 0x55555555u);
921 testUInt32Calc("~~0xaaaaaaaa", 0xaaaaaaaau);
922 testUInt32Calc("0xaaaaaaaa >> 8", 0xffaaaaaau);
923 testUInt32Calc("0xaaaaaaaa << 8", 0xaaaaaa00u);
924 // using integer literals assigned to variables
925 testUInt32Calc("a:=0xaaaaaaaa; b:=0xffff0000; a AND b", 0xaaaa0000u);
926 testUInt32Calc("a:=0xaaaaaaaa; b:=0xffff0000; a OR b", 0xffffaaaau);
927 testUInt32Calc("a:=0xaaaaaaaa; b:=0xffff0000; a XOR b", 0x5555aaaau);
928 testUInt32Calc("a:=0xaaaaaaaa; ~a", 0x55555555u);
929 testUInt32Calc("a:=0xaaaaaaaa; ~~a", 0xaaaaaaaau);
930 testUInt32Calc("a:=0xaaaaaaaa; a >> 8", 0xffaaaaaau);
931 testUInt32Calc("a:=0xaaaaaaaa; a << 8", 0xaaaaaa00u);
932
933 // Test proper conversion of double values (+ 0.1 enforces double literal)
934 // when used as inputs to the bitwise operations.
935 // 0xaaaaaaaa = -1431655766 or 2863311530u
936 testUInt32Calc("-1431655766.1 OR 0", 0xaaaaaaaau);
937 testUInt32Calc("2863311530.1 OR 0", 0xaaaaaaaau);
938 testUInt32Calc("0 OR -1431655766.1", 0xaaaaaaaau);
939 testUInt32Calc("0 OR 2863311530.1", 0xaaaaaaaau);
940 testUInt32Calc("-1431655766.1 XOR 0", 0xaaaaaaaau);
941 testUInt32Calc("2863311530.1 XOR 0", 0xaaaaaaaau);
942 testUInt32Calc("0 XOR -1431655766.1", 0xaaaaaaaau);
943 testUInt32Calc("0 XOR 2863311530.1", 0xaaaaaaaau);
944 testUInt32Calc("-1431655766.1 AND 0xffffffff", 0xaaaaaaaau);
945 testUInt32Calc("2863311530.1 AND 0xffffffff", 0xaaaaaaaau);
946 testUInt32Calc("0xffffffff AND -1431655766.1", 0xaaaaaaaau);
947 testUInt32Calc("0xffffffff AND 2863311530.1", 0xaaaaaaaau);
948 testUInt32Calc("~ -1431655766.1", 0x55555555u);
949 testUInt32Calc("~ 2863311530.1", 0x55555555u);
950 testUInt32Calc("-1431655766.1 >> 0", 0xaaaaaaaau);
951 testUInt32Calc("2863311530.1 >> 0", 0xaaaaaaaau);
952 testUInt32Calc("-1431655766.1 >> 0.1", 0xaaaaaaaau);
953 testUInt32Calc("2863311530.1 >> 0.1", 0xaaaaaaaau);
954 testUInt32Calc("-1431655766.1 << 0", 0xaaaaaaaau);
955 testUInt32Calc("2863311530.1 << 0", 0xaaaaaaaau);
956 testUInt32Calc("-1431655766.1 << 0.1", 0xaaaaaaaau);
957 testUInt32Calc("2863311530.1 << 0.1", 0xaaaaaaaau);
958
887 return testDone();959 return testDone();
888}960}
889961

Subscribers

People subscribed via source and target branches