xref: /netbsd-src/external/bsd/ntp/dist/tests/libntp/lfpfunc.c (revision 63aea4bd5b445e491ff0389fe27ec78b3099dba3)
1 /*	$NetBSD: lfpfunc.c,v 1.1.1.3 2015/10/23 17:47:45 christos Exp $	*/
2 
3 #include "config.h"
4 
5 #include "ntp_stdlib.h"
6 #include "ntp_fp.h"
7 
8 #include "unity.h"
9 
10 #include <float.h>
11 #include <math.h>
12 
13 
14 /* replaced TEST_ASSERT_EQUAL_MEMORY(&a, &b, sizeof(a)) with TEST_ASSERT_EQUAL_l_fp(a, b).
15    It's safer this way, because structs can be compared even if they aren't initiated
16    with memset (due to padding bytes).
17 */
18 #define TEST_ASSERT_EQUAL_l_fp(a, b) { \
19     TEST_ASSERT_EQUAL_MESSAGE(a.l_i, b.l_i, "Field l_i"); \
20     TEST_ASSERT_EQUAL_UINT_MESSAGE(a.l_uf, b.l_uf, "Field l_uf");	\
21 }
22 
23 
24 
25 typedef int bool; // typedef enum { FALSE, TRUE } boolean; -> can't use this because TRUE and FALSE are already defined
26 
27 
28 typedef struct  {
29 	uint32_t h, l;
30 } lfp_hl;
31 
32 
33 int l_fp_scmp(const l_fp first, const l_fp second);
34 int l_fp_ucmp(const l_fp first, l_fp second );
35 l_fp l_fp_init(int32 i, u_int32 f);
36 l_fp l_fp_add(const l_fp first, const l_fp second);
37 l_fp l_fp_subtract(const l_fp first, const l_fp second);
38 l_fp l_fp_negate(const l_fp first);
39 l_fp l_fp_abs(const l_fp first);
40 int l_fp_signum(const l_fp first);
41 double l_fp_convert_to_double(const l_fp first);
42 l_fp l_fp_init_from_double( double rhs);
43 void l_fp_swap(l_fp * first, l_fp *second);
44 bool l_isgt(const l_fp first, const l_fp second);
45 bool l_isgtu(const l_fp first, const l_fp second);
46 bool l_ishis(const l_fp first, const l_fp second);
47 bool l_isgeq(const l_fp first, const l_fp second);
48 bool l_isequ(const l_fp first, const l_fp second);
49 double eps(double d);
50 
51 
52 void test_AdditionLR(void);
53 void test_AdditionRL(void);
54 void test_SubtractionLR(void);
55 void test_SubtractionRL(void);
56 void test_Negation(void);
57 void test_Absolute(void);
58 void test_FDF_RoundTrip(void);
59 void test_SignedRelOps(void);
60 void test_UnsignedRelOps(void);
61 
62 
63 
64 static int cmp_work(u_int32 a[3], u_int32 b[3]);
65 
66 //----------------------------------------------------------------------
67 // reference comparision
68 // This is implementad as a full signed MP-subtract in 3 limbs, where
69 // the operands are zero or sign extended before the subtraction is
70 // executed.
71 //----------------------------------------------------------------------
72 
73 int
74 l_fp_scmp(const l_fp first, const l_fp second)
75 {
76 	u_int32 a[3], b[3];
77 
78 	const l_fp op1 = first;
79 	const l_fp op2 = second;
80 
81 	a[0] = op1.l_uf; a[1] = op1.l_ui; a[2] = 0;
82 	b[0] = op2.l_uf; b[1] = op2.l_ui; b[2] = 0;
83 
84 	a[2] -= (op1.l_i < 0);
85 	b[2] -= (op2.l_i < 0);
86 
87 	return cmp_work(a,b);
88 }
89 
90 int
91 l_fp_ucmp(const l_fp first, l_fp second )
92 {
93 	u_int32 a[3], b[3];
94 	const l_fp op1 = first;
95 	const l_fp op2 = second;
96 
97 	a[0] = op1.l_uf; a[1] = op1.l_ui; a[2] = 0;
98 	b[0] = op2.l_uf; b[1] = op2.l_ui; b[2] = 0;
99 
100 	return cmp_work(a,b);
101 }
102 
103 // maybe rename it to lf_cmp_work
104 int
105 cmp_work(u_int32 a[3], u_int32 b[3])
106 {
107 	u_int32 cy, idx, tmp;
108 	for (cy = idx = 0; idx < 3; ++idx) {
109 		tmp = a[idx]; cy  = (a[idx] -=   cy  ) > tmp;
110 		tmp = a[idx]; cy |= (a[idx] -= b[idx]) > tmp;
111 	}
112 	if (a[2])
113 		return -1;
114 	return a[0] || a[1];
115 }
116 
117 
118 //----------------------------------------------------------------------
119 // imlementation of the LFP stuff
120 // This should be easy enough...
121 //----------------------------------------------------------------------
122 
123 l_fp
124 l_fp_init(int32 i, u_int32 f)
125 {
126 	l_fp temp;
127 	temp.l_i  = i;
128 	temp.l_uf = f;
129 
130 	return temp;
131 }
132 
133 l_fp
134 l_fp_add(const l_fp first, const l_fp second)
135 {
136 	l_fp temp = first;
137 	L_ADD(&temp, &second);
138 
139 	return temp;
140 }
141 
142 l_fp
143 l_fp_subtract(const l_fp first, const l_fp second)
144 {
145 	l_fp temp = first;
146 	L_SUB(&temp, &second);
147 
148 	return temp;
149 }
150 
151 l_fp
152 l_fp_negate(const l_fp first)
153 {
154 	l_fp temp = first;
155 	L_NEG(&temp);
156 
157 	return temp;
158 }
159 
160 l_fp
161 l_fp_abs(const l_fp first)
162 {
163 	l_fp temp = first;
164 	if (L_ISNEG(&temp))
165 		L_NEG(&temp);
166 	return temp;
167 }
168 
169 int
170 l_fp_signum(const l_fp first)
171 {
172 	if (first.l_ui & 0x80000000u)
173 		return -1;
174 	return (first.l_ui || first.l_uf);
175 }
176 
177 double
178 l_fp_convert_to_double(const l_fp first)
179 {
180 	double res;
181 	LFPTOD(&first, res);
182 	return res;
183 }
184 
185 l_fp
186 l_fp_init_from_double( double rhs)
187 {
188 	l_fp temp;
189 	DTOLFP(rhs, &temp);
190 	return temp;
191 }
192 
193 void
194 l_fp_swap(l_fp * first, l_fp *second){
195 	l_fp temp = *second;
196 
197 	*second = *first;
198 	*first = temp;
199 }
200 
201 //----------------------------------------------------------------------
202 // testing the relational macros works better with proper predicate
203 // formatting functions; it slows down the tests a bit, but makes for
204 // readable failure messages.
205 //----------------------------------------------------------------------
206 
207 
208 bool
209 l_isgt (const l_fp first, const l_fp second) {
210 	return L_ISGT(&first, &second);
211 }
212 
213 bool
214 l_isgtu(const l_fp first, const l_fp second) {
215 	return L_ISGTU(&first, &second);
216 }
217 
218 bool
219 l_ishis(const l_fp first, const l_fp second) {
220 	return L_ISHIS(&first, &second);
221 }
222 
223 bool
224 l_isgeq(const l_fp first, const l_fp second) {
225 	return L_ISGEQ(&first, &second);
226 }
227 
228 bool
229 l_isequ(const l_fp first, const l_fp second) {
230 	return L_ISEQU(&first, &second);
231 }
232 
233 
234 //----------------------------------------------------------------------
235 // test data table for add/sub and compare
236 //----------------------------------------------------------------------
237 
238 
239 static const lfp_hl addsub_tab[][3] = {
240 	// trivial idendity:
241 	{{0 ,0         }, { 0,0         }, { 0,0}},
242 	// with carry from fraction and sign change:
243 	{{-1,0x80000000}, { 0,0x80000000}, { 0,0}},
244 	// without carry from fraction
245 	{{ 1,0x40000000}, { 1,0x40000000}, { 2,0x80000000}},
246 	// with carry from fraction:
247 	{{ 1,0xC0000000}, { 1,0xC0000000}, { 3,0x80000000}},
248 	// with carry from fraction and sign change:
249 	{{0x7FFFFFFF, 0x7FFFFFFF}, {0x7FFFFFFF,0x7FFFFFFF}, {0xFFFFFFFE,0xFFFFFFFE}},
250 	// two tests w/o carry (used for l_fp<-->double):
251 	{{0x55555555,0xAAAAAAAA}, {0x11111111,0x11111111}, {0x66666666,0xBBBBBBBB}},
252 	{{0x55555555,0x55555555}, {0x11111111,0x11111111}, {0x66666666,0x66666666}},
253 	// wide-range test, triggers compare trouble
254 	{{0x80000000,0x00000001}, {0xFFFFFFFF,0xFFFFFFFE}, {0x7FFFFFFF,0xFFFFFFFF}}
255 };
256 static const size_t addsub_cnt = (sizeof(addsub_tab)/sizeof(addsub_tab[0]));
257 static const size_t addsub_tot = (sizeof(addsub_tab)/sizeof(addsub_tab[0][0]));
258 
259 
260 
261 //----------------------------------------------------------------------
262 // epsilon estimation for the precision of a conversion double --> l_fp
263 //
264 // The error estimation limit is as follows:
265 //  * The 'l_fp' fixed point fraction has 32 bits precision, so we allow
266 //    for the LSB to toggle by clamping the epsilon to be at least 2^(-31)
267 //
268 //  * The double mantissa has a precsion 54 bits, so the other minimum is
269 //    dval * (2^(-53))
270 //
271 //  The maximum of those two boundaries is used for the check.
272 //
273 // Note: once there are more than 54 bits between the highest and lowest
274 // '1'-bit of the l_fp value, the roundtrip *will* create truncation
275 // errors. This is an inherent property caused by the 54-bit mantissa of
276 // the 'double' type.
277 double
278 eps(double d)
279 {
280 	return fmax(ldexp(1.0, -31), ldexp(fabs(d), -53));
281 }
282 
283 //----------------------------------------------------------------------
284 // test addition
285 //----------------------------------------------------------------------
286 void
287 test_AdditionLR(void) {
288 
289 	size_t idx = 0;
290 	for (idx = 0; idx < addsub_cnt; ++idx) {
291 		l_fp op1 = l_fp_init(addsub_tab[idx][0].h, addsub_tab[idx][0].l);
292 		l_fp op2 = l_fp_init(addsub_tab[idx][1].h, addsub_tab[idx][1].l);
293 		l_fp exp = l_fp_init(addsub_tab[idx][2].h, addsub_tab[idx][2].l);
294 		l_fp res = l_fp_add(op1, op2);
295 
296 		TEST_ASSERT_EQUAL_l_fp(exp, res);
297 	}
298 }
299 
300 void
301 test_AdditionRL(void) {
302 	size_t idx = 0;
303 	for (idx = 0; idx < addsub_cnt; ++idx) {
304 		l_fp op2 = l_fp_init(addsub_tab[idx][0].h, addsub_tab[idx][0].l);
305 		l_fp op1 = l_fp_init(addsub_tab[idx][1].h, addsub_tab[idx][1].l);
306 		l_fp exp = l_fp_init(addsub_tab[idx][2].h, addsub_tab[idx][2].l);
307 		l_fp res = l_fp_add(op1, op2);
308 
309 		TEST_ASSERT_EQUAL_l_fp(exp, res);
310 	}
311 }
312 
313 
314 
315 //----------------------------------------------------------------------
316 // test subtraction
317 //----------------------------------------------------------------------
318 void
319 test_SubtractionLR(void) {
320 	size_t idx = 0;
321 	for (idx = 0; idx < addsub_cnt; ++idx) {
322 		l_fp op2 = l_fp_init(addsub_tab[idx][0].h, addsub_tab[idx][0].l);
323 		l_fp exp = l_fp_init(addsub_tab[idx][1].h, addsub_tab[idx][1].l);
324 		l_fp op1 = l_fp_init(addsub_tab[idx][2].h, addsub_tab[idx][2].l);
325 		l_fp res = l_fp_subtract(op1, op2);
326 
327 		TEST_ASSERT_EQUAL_l_fp(exp, res);
328 	}
329 }
330 
331 void
332 test_SubtractionRL(void) {
333 	size_t idx = 0;
334 	for (idx = 0; idx < addsub_cnt; ++idx) {
335 		l_fp exp = l_fp_init(addsub_tab[idx][0].h, addsub_tab[idx][0].l);
336 		l_fp op2 = l_fp_init(addsub_tab[idx][1].h, addsub_tab[idx][1].l);
337 		l_fp op1 = l_fp_init(addsub_tab[idx][2].h, addsub_tab[idx][2].l);
338 		l_fp res = l_fp_subtract(op1, op2);
339 
340 		TEST_ASSERT_EQUAL_l_fp(exp, res);
341 	}
342 }
343 
344 //----------------------------------------------------------------------
345 // test negation
346 //----------------------------------------------------------------------
347 
348 void
349 test_Negation(void) {
350 
351 	size_t idx = 0;
352 	for (idx = 0; idx < addsub_cnt; ++idx) {
353 		l_fp op1 = l_fp_init(addsub_tab[idx][0].h, addsub_tab[idx][0].l);
354 		l_fp op2 = l_fp_negate(op1);
355 		l_fp sum = l_fp_add(op1, op2);
356 
357 		l_fp zero = l_fp_init(0, 0);
358 
359 		TEST_ASSERT_EQUAL_l_fp(zero, sum);
360 	}
361 }
362 
363 
364 
365 //----------------------------------------------------------------------
366 // test absolute value
367 //----------------------------------------------------------------------
368 void
369 test_Absolute(void) {
370 	size_t idx = 0;
371 	for (idx = 0; idx < addsub_cnt; ++idx) {
372 		l_fp op1 = l_fp_init(addsub_tab[idx][0].h, addsub_tab[idx][0].l);
373 		l_fp op2 = l_fp_abs(op1);
374 
375 		TEST_ASSERT_TRUE(l_fp_signum(op2) >= 0);
376 
377 		if (l_fp_signum(op1) >= 0)
378 			op1 = l_fp_subtract(op1, op2);
379 		else
380 			op1 = l_fp_add(op1, op2);
381 
382 		l_fp zero = l_fp_init(0, 0);
383 
384 		TEST_ASSERT_EQUAL_l_fp(zero, op1);
385 	}
386 
387 	// There is one special case we have to check: the minimum
388 	// value cannot be negated, or, to be more precise, the
389 	// negation reproduces the original pattern.
390 	l_fp minVal = l_fp_init(0x80000000, 0x00000000);
391 	l_fp minAbs = l_fp_abs(minVal);
392 	TEST_ASSERT_EQUAL(-1, l_fp_signum(minVal));
393 
394 	TEST_ASSERT_EQUAL_l_fp(minVal, minAbs);
395 }
396 
397 
398 //----------------------------------------------------------------------
399 // fp -> double -> fp rountrip test
400 //----------------------------------------------------------------------
401 void
402 test_FDF_RoundTrip(void) {
403 	// since a l_fp has 64 bits in it's mantissa and a double has
404 	// only 54 bits available (including the hidden '1') we have to
405 	// make a few concessions on the roundtrip precision. The 'eps()'
406 	// function makes an educated guess about the avilable precision
407 	// and checks the difference in the two 'l_fp' values against
408 	// that limit.
409 	size_t idx = 0;
410 	for (idx = 0; idx < addsub_cnt; ++idx) {
411 		l_fp op1 = l_fp_init(addsub_tab[idx][0].h, addsub_tab[idx][0].l);
412 		double op2 = l_fp_convert_to_double(op1);
413 		l_fp op3 = l_fp_init_from_double(op2);
414 
415 		l_fp temp = l_fp_subtract(op1, op3);
416 		double d = l_fp_convert_to_double(temp);
417  		TEST_ASSERT_DOUBLE_WITHIN(eps(op2), 0.0, fabs(d));
418 	}
419 }
420 
421 
422 //----------------------------------------------------------------------
423 // test the compare stuff
424 //
425 // This uses the local compare and checks if the operations using the
426 // macros in 'ntp_fp.h' produce mathing results.
427 // ----------------------------------------------------------------------
428 void
429 test_SignedRelOps(void) {
430 	const lfp_hl * tv = (&addsub_tab[0][0]);
431 	size_t lc ;
432 	for (lc = addsub_tot - 1; lc; --lc, ++tv) {
433 		l_fp op1 = l_fp_init(tv[0].h, tv[0].l);
434 		l_fp op2 = l_fp_init(tv[1].h, tv[1].l);
435 		int cmp = l_fp_scmp(op1, op2);
436 
437 		switch (cmp) {
438 		case -1:
439 			//printf("op1:%d %d, op2:%d %d\n",op1.l_uf,op1.l_ui,op2.l_uf,op2.l_ui);
440 			l_fp_swap(&op1, &op2);
441 			//printf("op1:%d %d, op2:%d %d\n",op1.l_uf,op1.l_ui,op2.l_uf,op2.l_ui);
442 		case 1:
443 			TEST_ASSERT_TRUE (l_isgt(op1, op2));
444 			TEST_ASSERT_FALSE(l_isgt(op2, op1));
445 
446 			TEST_ASSERT_TRUE (l_isgeq(op1, op2));
447 			TEST_ASSERT_FALSE(l_isgeq(op2, op1));
448 
449 			TEST_ASSERT_FALSE(l_isequ(op1, op2));
450 			TEST_ASSERT_FALSE(l_isequ(op2, op1));
451 			break;
452 		case 0:
453 			TEST_ASSERT_FALSE(l_isgt(op1, op2));
454 			TEST_ASSERT_FALSE(l_isgt(op2, op1));
455 
456 			TEST_ASSERT_TRUE (l_isgeq(op1, op2));
457 			TEST_ASSERT_TRUE (l_isgeq(op2, op1));
458 
459 			TEST_ASSERT_TRUE (l_isequ(op1, op2));
460 			TEST_ASSERT_TRUE (l_isequ(op2, op1));
461 			break;
462 		default:
463 			TEST_FAIL_MESSAGE("unexpected UCMP result: " );
464 		}
465 	}
466 }
467 
468 void
469 test_UnsignedRelOps(void) {
470 	const lfp_hl * tv =(&addsub_tab[0][0]);
471 	size_t lc;
472 	for (lc = addsub_tot - 1; lc; --lc, ++tv) {
473 		l_fp op1 = l_fp_init(tv[0].h, tv[0].l);
474 		l_fp op2 = l_fp_init(tv[1].h, tv[1].l);
475 		int cmp = l_fp_ucmp(op1, op2);
476 
477 		switch (cmp) {
478 		case -1:
479 			//printf("op1:%d %d, op2:%d %d\n",op1.l_uf,op1.l_ui,op2.l_uf,op2.l_ui);
480 			l_fp_swap(&op1, &op2);
481 			//printf("op1:%d %d, op2:%d %d\n",op1.l_uf,op1.l_ui,op2.l_uf,op2.l_ui);
482 		case 1:
483 			TEST_ASSERT_TRUE (l_isgtu(op1, op2));
484 			TEST_ASSERT_FALSE(l_isgtu(op2, op1));
485 
486 			TEST_ASSERT_TRUE (l_ishis(op1, op2));
487 			TEST_ASSERT_FALSE(l_ishis(op2, op1));
488 			break;
489 		case 0:
490 			TEST_ASSERT_FALSE(l_isgtu(op1, op2));
491 			TEST_ASSERT_FALSE(l_isgtu(op2, op1));
492 
493 			TEST_ASSERT_TRUE (l_ishis(op1, op2));
494 			TEST_ASSERT_TRUE (l_ishis(op2, op1));
495 			break;
496 		default:
497 			TEST_FAIL_MESSAGE("unexpected UCMP result: " );
498 		}
499 	}
500 }
501 /*
502 */
503 
504 //----------------------------------------------------------------------
505 // that's all folks... but feel free to add things!
506 //----------------------------------------------------------------------
507