summaryrefslogtreecommitdiffstats
path: root/src/tools/testint128.c
blob: 71c345969a854b286870bd41d41f170cc96e27e3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
/*-------------------------------------------------------------------------
 *
 * testint128.c
 *	  Testbed for roll-our-own 128-bit integer arithmetic.
 *
 * This is a standalone test program that compares the behavior of an
 * implementation in int128.h to an (assumed correct) int128 native type.
 *
 * Copyright (c) 2017-2021, PostgreSQL Global Development Group
 *
 *
 * IDENTIFICATION
 *	  src/tools/testint128.c
 *
 *-------------------------------------------------------------------------
 */

#include "postgres_fe.h"

/*
 * By default, we test the non-native implementation in int128.h; but
 * by predefining USE_NATIVE_INT128 to 1, you can test the native
 * implementation, just to be sure.
 */
#ifndef USE_NATIVE_INT128
#define USE_NATIVE_INT128 0
#endif

#include "common/int128.h"

/*
 * We assume the parts of this union are laid out compatibly.
 */
typedef union
{
	int128		i128;
	INT128		I128;
	union
	{
#ifdef WORDS_BIGENDIAN
		int64		hi;
		uint64		lo;
#else
		uint64		lo;
		int64		hi;
#endif
	}			hl;
}			test128;


/*
 * Control version of comparator.
 */
static inline int
my_int128_compare(int128 x, int128 y)
{
	if (x < y)
		return -1;
	if (x > y)
		return 1;
	return 0;
}

/*
 * Get a random uint64 value.
 * We don't assume random() is good for more than 16 bits.
 */
static uint64
get_random_uint64(void)
{
	uint64		x;

	x = (uint64) (random() & 0xFFFF) << 48;
	x |= (uint64) (random() & 0xFFFF) << 32;
	x |= (uint64) (random() & 0xFFFF) << 16;
	x |= (uint64) (random() & 0xFFFF);
	return x;
}

/*
 * Main program.
 *
 * Generates a lot of random numbers and tests the implementation for each.
 * The results should be reproducible, since we don't call srandom().
 *
 * You can give a loop count if you don't like the default 1B iterations.
 */
int
main(int argc, char **argv)
{
	long		count;

	if (argc >= 2)
		count = strtol(argv[1], NULL, 0);
	else
		count = 1000000000;

	while (count-- > 0)
	{
		int64		x = get_random_uint64();
		int64		y = get_random_uint64();
		int64		z = get_random_uint64();
		test128		t1;
		test128		t2;

		/* check unsigned addition */
		t1.hl.hi = x;
		t1.hl.lo = y;
		t2 = t1;
		t1.i128 += (int128) (uint64) z;
		int128_add_uint64(&t2.I128, (uint64) z);

		if (t1.hl.hi != t2.hl.hi || t1.hl.lo != t2.hl.lo)
		{
			printf("%016lX%016lX + unsigned %lX\n", x, y, z);
			printf("native = %016lX%016lX\n", t1.hl.hi, t1.hl.lo);
			printf("result = %016lX%016lX\n", t2.hl.hi, t2.hl.lo);
			return 1;
		}

		/* check signed addition */
		t1.hl.hi = x;
		t1.hl.lo = y;
		t2 = t1;
		t1.i128 += (int128) z;
		int128_add_int64(&t2.I128, z);

		if (t1.hl.hi != t2.hl.hi || t1.hl.lo != t2.hl.lo)
		{
			printf("%016lX%016lX + signed %lX\n", x, y, z);
			printf("native = %016lX%016lX\n", t1.hl.hi, t1.hl.lo);
			printf("result = %016lX%016lX\n", t2.hl.hi, t2.hl.lo);
			return 1;
		}

		/* check multiplication */
		t1.i128 = (int128) x * (int128) y;

		t2.hl.hi = t2.hl.lo = 0;
		int128_add_int64_mul_int64(&t2.I128, x, y);

		if (t1.hl.hi != t2.hl.hi || t1.hl.lo != t2.hl.lo)
		{
			printf("%lX * %lX\n", x, y);
			printf("native = %016lX%016lX\n", t1.hl.hi, t1.hl.lo);
			printf("result = %016lX%016lX\n", t2.hl.hi, t2.hl.lo);
			return 1;
		}

		/* check comparison */
		t1.hl.hi = x;
		t1.hl.lo = y;
		t2.hl.hi = z;
		t2.hl.lo = get_random_uint64();

		if (my_int128_compare(t1.i128, t2.i128) !=
			int128_compare(t1.I128, t2.I128))
		{
			printf("comparison failure: %d vs %d\n",
				   my_int128_compare(t1.i128, t2.i128),
				   int128_compare(t1.I128, t2.I128));
			printf("arg1 = %016lX%016lX\n", t1.hl.hi, t1.hl.lo);
			printf("arg2 = %016lX%016lX\n", t2.hl.hi, t2.hl.lo);
			return 1;
		}

		/* check case with identical hi parts; above will hardly ever hit it */
		t2.hl.hi = x;

		if (my_int128_compare(t1.i128, t2.i128) !=
			int128_compare(t1.I128, t2.I128))
		{
			printf("comparison failure: %d vs %d\n",
				   my_int128_compare(t1.i128, t2.i128),
				   int128_compare(t1.I128, t2.I128));
			printf("arg1 = %016lX%016lX\n", t1.hl.hi, t1.hl.lo);
			printf("arg2 = %016lX%016lX\n", t2.hl.hi, t2.hl.lo);
			return 1;
		}
	}

	return 0;
}