1 /*-------------------------------------------------------------------------
2  *
3  * testint128.c
4  *	  Testbed for roll-our-own 128-bit integer arithmetic.
5  *
6  * This is a standalone test program that compares the behavior of an
7  * implementation in int128.h to an (assumed correct) int128 native type.
8  *
9  * Copyright (c) 2017, PostgreSQL Global Development Group
10  *
11  *
12  * IDENTIFICATION
13  *	  src/tools/testint128.c
14  *
15  *-------------------------------------------------------------------------
16  */
17 
18 #include "postgres_fe.h"
19 
20 /*
21  * By default, we test the non-native implementation in int128.h; but
22  * by predefining USE_NATIVE_INT128 to 1, you can test the native
23  * implementation, just to be sure.
24  */
25 #ifndef USE_NATIVE_INT128
26 #define USE_NATIVE_INT128 0
27 #endif
28 
29 #include "common/int128.h"
30 
31 /*
32  * We assume the parts of this union are laid out compatibly.
33  */
34 typedef union
35 {
36 	int128		i128;
37 	INT128		I128;
38 	union
39 	{
40 #ifdef WORDS_BIGENDIAN
41 		int64		hi;
42 		uint64		lo;
43 #else
44 		uint64		lo;
45 		int64		hi;
46 #endif
47 	}			hl;
48 }			test128;
49 
50 
51 /*
52  * Control version of comparator.
53  */
54 static inline int
my_int128_compare(int128 x,int128 y)55 my_int128_compare(int128 x, int128 y)
56 {
57 	if (x < y)
58 		return -1;
59 	if (x > y)
60 		return 1;
61 	return 0;
62 }
63 
64 /*
65  * Get a random uint64 value.
66  * We don't assume random() is good for more than 16 bits.
67  */
68 static uint64
get_random_uint64(void)69 get_random_uint64(void)
70 {
71 	uint64		x;
72 
73 	x = (uint64) (random() & 0xFFFF) << 48;
74 	x |= (uint64) (random() & 0xFFFF) << 32;
75 	x |= (uint64) (random() & 0xFFFF) << 16;
76 	x |= (uint64) (random() & 0xFFFF);
77 	return x;
78 }
79 
80 /*
81  * Main program.
82  *
83  * Generates a lot of random numbers and tests the implementation for each.
84  * The results should be reproducible, since we don't call srandom().
85  *
86  * You can give a loop count if you don't like the default 1B iterations.
87  */
88 int
main(int argc,char ** argv)89 main(int argc, char **argv)
90 {
91 	long		count;
92 
93 	if (argc >= 2)
94 		count = strtol(argv[1], NULL, 0);
95 	else
96 		count = 1000000000;
97 
98 	while (count-- > 0)
99 	{
100 		int64		x = get_random_uint64();
101 		int64		y = get_random_uint64();
102 		int64		z = get_random_uint64();
103 		test128		t1;
104 		test128		t2;
105 
106 		/* check unsigned addition */
107 		t1.hl.hi = x;
108 		t1.hl.lo = y;
109 		t2 = t1;
110 		t1.i128 += (int128) (uint64) z;
111 		int128_add_uint64(&t2.I128, (uint64) z);
112 
113 		if (t1.hl.hi != t2.hl.hi || t1.hl.lo != t2.hl.lo)
114 		{
115 			printf("%016lX%016lX + unsigned %lX\n", x, y, z);
116 			printf("native = %016lX%016lX\n", t1.hl.hi, t1.hl.lo);
117 			printf("result = %016lX%016lX\n", t2.hl.hi, t2.hl.lo);
118 			return 1;
119 		}
120 
121 		/* check signed addition */
122 		t1.hl.hi = x;
123 		t1.hl.lo = y;
124 		t2 = t1;
125 		t1.i128 += (int128) z;
126 		int128_add_int64(&t2.I128, z);
127 
128 		if (t1.hl.hi != t2.hl.hi || t1.hl.lo != t2.hl.lo)
129 		{
130 			printf("%016lX%016lX + signed %lX\n", x, y, z);
131 			printf("native = %016lX%016lX\n", t1.hl.hi, t1.hl.lo);
132 			printf("result = %016lX%016lX\n", t2.hl.hi, t2.hl.lo);
133 			return 1;
134 		}
135 
136 		/* check multiplication */
137 		t1.i128 = (int128) x * (int128) y;
138 
139 		t2.hl.hi = t2.hl.lo = 0;
140 		int128_add_int64_mul_int64(&t2.I128, x, y);
141 
142 		if (t1.hl.hi != t2.hl.hi || t1.hl.lo != t2.hl.lo)
143 		{
144 			printf("%lX * %lX\n", x, y);
145 			printf("native = %016lX%016lX\n", t1.hl.hi, t1.hl.lo);
146 			printf("result = %016lX%016lX\n", t2.hl.hi, t2.hl.lo);
147 			return 1;
148 		}
149 
150 		/* check comparison */
151 		t1.hl.hi = x;
152 		t1.hl.lo = y;
153 		t2.hl.hi = z;
154 		t2.hl.lo = get_random_uint64();
155 
156 		if (my_int128_compare(t1.i128, t2.i128) !=
157 			int128_compare(t1.I128, t2.I128))
158 		{
159 			printf("comparison failure: %d vs %d\n",
160 				   my_int128_compare(t1.i128, t2.i128),
161 				   int128_compare(t1.I128, t2.I128));
162 			printf("arg1 = %016lX%016lX\n", t1.hl.hi, t1.hl.lo);
163 			printf("arg2 = %016lX%016lX\n", t2.hl.hi, t2.hl.lo);
164 			return 1;
165 		}
166 
167 		/* check case with identical hi parts; above will hardly ever hit it */
168 		t2.hl.hi = x;
169 
170 		if (my_int128_compare(t1.i128, t2.i128) !=
171 			int128_compare(t1.I128, t2.I128))
172 		{
173 			printf("comparison failure: %d vs %d\n",
174 				   my_int128_compare(t1.i128, t2.i128),
175 				   int128_compare(t1.I128, t2.I128));
176 			printf("arg1 = %016lX%016lX\n", t1.hl.hi, t1.hl.lo);
177 			printf("arg2 = %016lX%016lX\n", t2.hl.hi, t2.hl.lo);
178 			return 1;
179 		}
180 	}
181 
182 	return 0;
183 }
184