3 #include "ntp_stdlib.h"
12 /* replaced TEST_ASSERT_EQUAL_MEMORY(&a, &b, sizeof(a)) with TEST_ASSERT_EQUAL_l_fp(a, b).
13 It's safer this way, because structs can be compared even if they aren't initiated
14 with memset (due to padding bytes).
16 #define TEST_ASSERT_EQUAL_l_fp(a, b) { \
17 TEST_ASSERT_EQUAL_MESSAGE(a.l_i, b.l_i, "Field l_i"); \
18 TEST_ASSERT_EQUAL_UINT_MESSAGE(a.l_uf, b.l_uf, "Field l_uf"); \
23 typedef int bool; // typedef enum { FALSE, TRUE } boolean; -> can't use this because TRUE and FALSE are already defined
31 int l_fp_scmp(const l_fp first, const l_fp second);
32 int l_fp_ucmp(const l_fp first, l_fp second );
33 l_fp l_fp_init(int32 i, u_int32 f);
34 l_fp l_fp_add(const l_fp first, const l_fp second);
35 l_fp l_fp_subtract(const l_fp first, const l_fp second);
36 l_fp l_fp_negate(const l_fp first);
37 l_fp l_fp_abs(const l_fp first);
38 int l_fp_signum(const l_fp first);
39 double l_fp_convert_to_double(const l_fp first);
40 l_fp l_fp_init_from_double( double rhs);
41 void l_fp_swap(l_fp * first, l_fp *second);
42 bool l_isgt(const l_fp first, const l_fp second);
43 bool l_isgtu(const l_fp first, const l_fp second);
44 bool l_ishis(const l_fp first, const l_fp second);
45 bool l_isgeq(const l_fp first, const l_fp second);
46 bool l_isequ(const l_fp first, const l_fp second);
50 void test_AdditionLR(void);
51 void test_AdditionRL(void);
52 void test_SubtractionLR(void);
53 void test_SubtractionRL(void);
54 void test_Negation(void);
55 void test_Absolute(void);
56 void test_FDF_RoundTrip(void);
57 void test_SignedRelOps(void);
58 void test_UnsignedRelOps(void);
62 static int cmp_work(u_int32 a[3], u_int32 b[3]);
64 //----------------------------------------------------------------------
65 // reference comparision
66 // This is implementad as a full signed MP-subtract in 3 limbs, where
67 // the operands are zero or sign extended before the subtraction is
69 //----------------------------------------------------------------------
72 l_fp_scmp(const l_fp first, const l_fp second)
76 const l_fp op1 = first;
77 const l_fp op2 = second;
79 a[0] = op1.l_uf; a[1] = op1.l_ui; a[2] = 0;
80 b[0] = op2.l_uf; b[1] = op2.l_ui; b[2] = 0;
82 a[2] -= (op1.l_i < 0);
83 b[2] -= (op2.l_i < 0);
89 l_fp_ucmp(const l_fp first, l_fp second )
92 const l_fp op1 = first;
93 const l_fp op2 = second;
95 a[0] = op1.l_uf; a[1] = op1.l_ui; a[2] = 0;
96 b[0] = op2.l_uf; b[1] = op2.l_ui; b[2] = 0;
101 // maybe rename it to lf_cmp_work
103 cmp_work(u_int32 a[3], u_int32 b[3])
105 u_int32 cy, idx, tmp;
106 for (cy = idx = 0; idx < 3; ++idx) {
107 tmp = a[idx]; cy = (a[idx] -= cy ) > tmp;
108 tmp = a[idx]; cy |= (a[idx] -= b[idx]) > tmp;
116 //----------------------------------------------------------------------
117 // imlementation of the LFP stuff
118 // This should be easy enough...
119 //----------------------------------------------------------------------
122 l_fp_init(int32 i, u_int32 f)
132 l_fp_add(const l_fp first, const l_fp second)
135 L_ADD(&temp, &second);
141 l_fp_subtract(const l_fp first, const l_fp second)
144 L_SUB(&temp, &second);
150 l_fp_negate(const l_fp first)
159 l_fp_abs(const l_fp first)
168 l_fp_signum(const l_fp first)
170 if (first.l_ui & 0x80000000u)
172 return (first.l_ui || first.l_uf);
176 l_fp_convert_to_double(const l_fp first)
184 l_fp_init_from_double( double rhs)
192 l_fp_swap(l_fp * first, l_fp *second){
199 //----------------------------------------------------------------------
200 // testing the relational macros works better with proper predicate
201 // formatting functions; it slows down the tests a bit, but makes for
202 // readable failure messages.
203 //----------------------------------------------------------------------
207 l_isgt (const l_fp first, const l_fp second) {
208 return L_ISGT(&first, &second);
212 l_isgtu(const l_fp first, const l_fp second) {
213 return L_ISGTU(&first, &second);
217 l_ishis(const l_fp first, const l_fp second) {
218 return L_ISHIS(&first, &second);
222 l_isgeq(const l_fp first, const l_fp second) {
223 return L_ISGEQ(&first, &second);
227 l_isequ(const l_fp first, const l_fp second) {
228 return L_ISEQU(&first, &second);
232 //----------------------------------------------------------------------
233 // test data table for add/sub and compare
234 //----------------------------------------------------------------------
237 static const lfp_hl addsub_tab[][3] = {
239 {{0 ,0 }, { 0,0 }, { 0,0}},
240 // with carry from fraction and sign change:
241 {{-1,0x80000000}, { 0,0x80000000}, { 0,0}},
242 // without carry from fraction
243 {{ 1,0x40000000}, { 1,0x40000000}, { 2,0x80000000}},
244 // with carry from fraction:
245 {{ 1,0xC0000000}, { 1,0xC0000000}, { 3,0x80000000}},
246 // with carry from fraction and sign change:
247 {{0x7FFFFFFF, 0x7FFFFFFF}, {0x7FFFFFFF,0x7FFFFFFF}, {0xFFFFFFFE,0xFFFFFFFE}},
248 // two tests w/o carry (used for l_fp<-->double):
249 {{0x55555555,0xAAAAAAAA}, {0x11111111,0x11111111}, {0x66666666,0xBBBBBBBB}},
250 {{0x55555555,0x55555555}, {0x11111111,0x11111111}, {0x66666666,0x66666666}},
251 // wide-range test, triggers compare trouble
252 {{0x80000000,0x00000001}, {0xFFFFFFFF,0xFFFFFFFE}, {0x7FFFFFFF,0xFFFFFFFF}}
254 static const size_t addsub_cnt = (sizeof(addsub_tab)/sizeof(addsub_tab[0]));
255 static const size_t addsub_tot = (sizeof(addsub_tab)/sizeof(addsub_tab[0][0]));
259 //----------------------------------------------------------------------
260 // epsilon estimation for the precision of a conversion double --> l_fp
262 // The error estimation limit is as follows:
263 // * The 'l_fp' fixed point fraction has 32 bits precision, so we allow
264 // for the LSB to toggle by clamping the epsilon to be at least 2^(-31)
266 // * The double mantissa has a precsion 54 bits, so the other minimum is
269 // The maximum of those two boundaries is used for the check.
271 // Note: once there are more than 54 bits between the highest and lowest
272 // '1'-bit of the l_fp value, the roundtrip *will* create truncation
273 // errors. This is an inherent property caused by the 54-bit mantissa of
274 // the 'double' type.
278 return fmax(ldexp(1.0, -31), ldexp(fabs(d), -53));
281 //----------------------------------------------------------------------
283 //----------------------------------------------------------------------
285 test_AdditionLR(void) {
288 for (idx = 0; idx < addsub_cnt; ++idx) {
289 l_fp op1 = l_fp_init(addsub_tab[idx][0].h, addsub_tab[idx][0].l);
290 l_fp op2 = l_fp_init(addsub_tab[idx][1].h, addsub_tab[idx][1].l);
291 l_fp exp = l_fp_init(addsub_tab[idx][2].h, addsub_tab[idx][2].l);
292 l_fp res = l_fp_add(op1, op2);
294 TEST_ASSERT_EQUAL_l_fp(exp, res);
299 test_AdditionRL(void) {
301 for (idx = 0; idx < addsub_cnt; ++idx) {
302 l_fp op2 = l_fp_init(addsub_tab[idx][0].h, addsub_tab[idx][0].l);
303 l_fp op1 = l_fp_init(addsub_tab[idx][1].h, addsub_tab[idx][1].l);
304 l_fp exp = l_fp_init(addsub_tab[idx][2].h, addsub_tab[idx][2].l);
305 l_fp res = l_fp_add(op1, op2);
307 TEST_ASSERT_EQUAL_l_fp(exp, res);
313 //----------------------------------------------------------------------
315 //----------------------------------------------------------------------
317 test_SubtractionLR(void) {
319 for (idx = 0; idx < addsub_cnt; ++idx) {
320 l_fp op2 = l_fp_init(addsub_tab[idx][0].h, addsub_tab[idx][0].l);
321 l_fp exp = l_fp_init(addsub_tab[idx][1].h, addsub_tab[idx][1].l);
322 l_fp op1 = l_fp_init(addsub_tab[idx][2].h, addsub_tab[idx][2].l);
323 l_fp res = l_fp_subtract(op1, op2);
325 TEST_ASSERT_EQUAL_l_fp(exp, res);
330 test_SubtractionRL(void) {
332 for (idx = 0; idx < addsub_cnt; ++idx) {
333 l_fp exp = l_fp_init(addsub_tab[idx][0].h, addsub_tab[idx][0].l);
334 l_fp op2 = l_fp_init(addsub_tab[idx][1].h, addsub_tab[idx][1].l);
335 l_fp op1 = l_fp_init(addsub_tab[idx][2].h, addsub_tab[idx][2].l);
336 l_fp res = l_fp_subtract(op1, op2);
338 TEST_ASSERT_EQUAL_l_fp(exp, res);
342 //----------------------------------------------------------------------
344 //----------------------------------------------------------------------
347 test_Negation(void) {
350 for (idx = 0; idx < addsub_cnt; ++idx) {
351 l_fp op1 = l_fp_init(addsub_tab[idx][0].h, addsub_tab[idx][0].l);
352 l_fp op2 = l_fp_negate(op1);
353 l_fp sum = l_fp_add(op1, op2);
355 l_fp zero = l_fp_init(0, 0);
357 TEST_ASSERT_EQUAL_l_fp(zero, sum);
363 //----------------------------------------------------------------------
364 // test absolute value
365 //----------------------------------------------------------------------
367 test_Absolute(void) {
369 for (idx = 0; idx < addsub_cnt; ++idx) {
370 l_fp op1 = l_fp_init(addsub_tab[idx][0].h, addsub_tab[idx][0].l);
371 l_fp op2 = l_fp_abs(op1);
373 TEST_ASSERT_TRUE(l_fp_signum(op2) >= 0);
375 if (l_fp_signum(op1) >= 0)
376 op1 = l_fp_subtract(op1, op2);
378 op1 = l_fp_add(op1, op2);
380 l_fp zero = l_fp_init(0, 0);
382 TEST_ASSERT_EQUAL_l_fp(zero, op1);
385 // There is one special case we have to check: the minimum
386 // value cannot be negated, or, to be more precise, the
387 // negation reproduces the original pattern.
388 l_fp minVal = l_fp_init(0x80000000, 0x00000000);
389 l_fp minAbs = l_fp_abs(minVal);
390 TEST_ASSERT_EQUAL(-1, l_fp_signum(minVal));
392 TEST_ASSERT_EQUAL_l_fp(minVal, minAbs);
396 //----------------------------------------------------------------------
397 // fp -> double -> fp rountrip test
398 //----------------------------------------------------------------------
400 test_FDF_RoundTrip(void) {
401 // since a l_fp has 64 bits in it's mantissa and a double has
402 // only 54 bits available (including the hidden '1') we have to
403 // make a few concessions on the roundtrip precision. The 'eps()'
404 // function makes an educated guess about the avilable precision
405 // and checks the difference in the two 'l_fp' values against
408 for (idx = 0; idx < addsub_cnt; ++idx) {
409 l_fp op1 = l_fp_init(addsub_tab[idx][0].h, addsub_tab[idx][0].l);
410 double op2 = l_fp_convert_to_double(op1);
411 l_fp op3 = l_fp_init_from_double(op2);
413 l_fp temp = l_fp_subtract(op1, op3);
414 double d = l_fp_convert_to_double(temp);
415 TEST_ASSERT_DOUBLE_WITHIN(eps(op2), 0.0, fabs(d));
420 //----------------------------------------------------------------------
421 // test the compare stuff
423 // This uses the local compare and checks if the operations using the
424 // macros in 'ntp_fp.h' produce mathing results.
425 // ----------------------------------------------------------------------
427 test_SignedRelOps(void) {
428 const lfp_hl * tv = (&addsub_tab[0][0]);
430 for (lc = addsub_tot - 1; lc; --lc, ++tv) {
431 l_fp op1 = l_fp_init(tv[0].h, tv[0].l);
432 l_fp op2 = l_fp_init(tv[1].h, tv[1].l);
433 int cmp = l_fp_scmp(op1, op2);
437 //printf("op1:%d %d, op2:%d %d\n",op1.l_uf,op1.l_ui,op2.l_uf,op2.l_ui);
438 l_fp_swap(&op1, &op2);
439 //printf("op1:%d %d, op2:%d %d\n",op1.l_uf,op1.l_ui,op2.l_uf,op2.l_ui);
441 TEST_ASSERT_TRUE (l_isgt(op1, op2));
442 TEST_ASSERT_FALSE(l_isgt(op2, op1));
444 TEST_ASSERT_TRUE (l_isgeq(op1, op2));
445 TEST_ASSERT_FALSE(l_isgeq(op2, op1));
447 TEST_ASSERT_FALSE(l_isequ(op1, op2));
448 TEST_ASSERT_FALSE(l_isequ(op2, op1));
451 TEST_ASSERT_FALSE(l_isgt(op1, op2));
452 TEST_ASSERT_FALSE(l_isgt(op2, op1));
454 TEST_ASSERT_TRUE (l_isgeq(op1, op2));
455 TEST_ASSERT_TRUE (l_isgeq(op2, op1));
457 TEST_ASSERT_TRUE (l_isequ(op1, op2));
458 TEST_ASSERT_TRUE (l_isequ(op2, op1));
461 TEST_FAIL_MESSAGE("unexpected UCMP result: " );
467 test_UnsignedRelOps(void) {
468 const lfp_hl * tv =(&addsub_tab[0][0]);
470 for (lc = addsub_tot - 1; lc; --lc, ++tv) {
471 l_fp op1 = l_fp_init(tv[0].h, tv[0].l);
472 l_fp op2 = l_fp_init(tv[1].h, tv[1].l);
473 int cmp = l_fp_ucmp(op1, op2);
477 //printf("op1:%d %d, op2:%d %d\n",op1.l_uf,op1.l_ui,op2.l_uf,op2.l_ui);
478 l_fp_swap(&op1, &op2);
479 //printf("op1:%d %d, op2:%d %d\n",op1.l_uf,op1.l_ui,op2.l_uf,op2.l_ui);
481 TEST_ASSERT_TRUE (l_isgtu(op1, op2));
482 TEST_ASSERT_FALSE(l_isgtu(op2, op1));
484 TEST_ASSERT_TRUE (l_ishis(op1, op2));
485 TEST_ASSERT_FALSE(l_ishis(op2, op1));
488 TEST_ASSERT_FALSE(l_isgtu(op1, op2));
489 TEST_ASSERT_FALSE(l_isgtu(op2, op1));
491 TEST_ASSERT_TRUE (l_ishis(op1, op2));
492 TEST_ASSERT_TRUE (l_ishis(op2, op1));
495 TEST_FAIL_MESSAGE("unexpected UCMP result: " );
502 //----------------------------------------------------------------------
503 // that's all folks... but feel free to add things!
504 //----------------------------------------------------------------------