Skip to content

Commit 0fc819b

Browse files
committed
finished test suite and added coverage script
1 parent 2887887 commit 0fc819b

File tree

5 files changed

+119
-21
lines changed

5 files changed

+119
-21
lines changed

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,8 @@ test.o
33
test.exe
44
test.obj
55
test
6+
coverage-html
7+
*.gcov
8+
*.gcda
9+
*.gcno
10+
coverage.info

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
128 Bit Integer Library For C
1+
128-Bit Integer Library For C
22
=============================
33

44
Simply include `int128.h` to use `int128` or `uint128`. Each operation on these

gen_coverage.sh

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#! /usr/bin/env sh
2+
# requires gcc with coverage support, gcov, lcov, and genhtml
3+
4+
gcc -Wall -pedantic-errors -std=c99 -Werror -fsanitize=address,undefined -g test.c -o test --coverage
5+
./test
6+
gcov test.c
7+
lcov --capture --directory . --output-file coverage.info --rc lcov_branch_coverage=1
8+
genhtml coverage.info --output-directory coverage-html --rc genhtml_branch_coverage=1

int128.h

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -284,9 +284,15 @@ static inline int128 int128_shiftr(int128 lhs, int rhs)
284284
return (int128){ .low = result.low, .high = result.high };
285285
}
286286

287-
if (rhs >= 64) {
287+
if (rhs > 64) {
288+
return (int128){
289+
.low = (lhs.high >> (rhs - 64)) |
290+
(UINT64_C(~0) << (64 - (rhs - 64))),
291+
.high = UINT64_C(~0),
292+
};
293+
} else if (rhs == 64) {
288294
return (int128){
289-
.low = (lhs.high >> (rhs - 64)) | (UINT64_C(~0) << rhs),
295+
.low = lhs.high,
290296
.high = UINT64_C(~0),
291297
};
292298
}
@@ -491,7 +497,7 @@ static inline uint128 uint128_div(uint128 lhs, uint128 rhs)
491497
}
492498

493499
// Returns lhs % rhs.
494-
static inline uint128 uint128_mod(uint128 lhs, uint128 rhs)
500+
static inline uint128 uint128_rem(uint128 lhs, uint128 rhs)
495501
{
496502
// Based on the algorithm described here:
497503
// https://stackoverflow.com/questions/5386377/division-without-using
@@ -531,6 +537,10 @@ static inline uint128 uint128_mod(uint128 lhs, uint128 rhs)
531537
static inline int128 int128_div(int128 lhs, int128 rhs)
532538
{
533539
// signed integers aren't symmetric. INT128_MIN == (-INT128_MAX) - 1
540+
if (int128_eq(lhs, rhs) && int128_eq(lhs, INT128_MIN)) {
541+
return INT128_C(1);
542+
}
543+
534544
if (int128_eq(lhs, INT128_MIN)) {
535545
return INT128_C(0);
536546
}
@@ -559,30 +569,33 @@ static inline int128 int128_div(int128 lhs, int128 rhs)
559569
}
560570

561571
// Returns lhs % rhs.
562-
static inline int128 int128_mod(int128 lhs, int128 rhs)
572+
static inline int128 int128_rem(int128 lhs, int128 rhs)
563573
{
564574
// signed integers aren't symmetric. INT128_MIN == (-INT128_MAX) - 1
565575
if (int128_eq(lhs, INT128_MIN)) {
566576
return INT128_C(0);
567577
}
568578

579+
if (int128_eq(rhs, INT128_MIN)) {
580+
return lhs;
581+
}
582+
569583
bool result_negative = false;
570584
if (int128_less(lhs, INT128_C(0))) {
571-
result_negative = !result_negative;
585+
result_negative = true;
572586
lhs = int128_neg(lhs);
573587
}
574588

575589
if (int128_less(rhs, INT128_C(0))) {
576-
result_negative = !result_negative;
577590
rhs = int128_neg(rhs);
578591
}
579592

580593
uint128 a = { .low = lhs.low, .high = lhs.high };
581594
uint128 b = { .low = rhs.low, .high = rhs.high };
582-
uint128 c = uint128_mod(a, b);
595+
uint128 c = uint128_rem(a, b);
583596
int128 result = { .low = c.low, .high = c.high };
584597

585-
if (result_negative) {
598+
if (result_negative && int128_greater(result, INT128_C(0))) {
586599
return int128_neg(result);
587600
}
588601

test.c

Lines changed: 84 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,6 @@
44

55
#include "int128.h"
66

7-
// void int128_printhex(char *name, int128 x)
8-
// {
9-
// printf("%s = %016" PRIx64 " %016" PRIx64 "\n", name, x.high, x.low);
10-
// }
11-
//
12-
// void uint128_printhex(char *name, uint128 x)
13-
// {
14-
// printf("%s = %016" PRIx64 " %016" PRIx64 "\n", name, x.high, x.low);
15-
// }
16-
177
void int128_tests()
188
{
199
// Left Shift
@@ -25,12 +15,28 @@ void int128_tests()
2515
b = int128_shiftr(a, 1);
2616
assert(b.low == (UINT64_C(1) << 63));
2717

18+
// Left shift of more than 63 bits?
19+
assert(int128_eq(int128_shiftl(INT128_C(1), 64),
20+
(int128){ .high = 1 }));
21+
2822
// Right Shift
2923
// For int128, this should be an arithmetic shift, meaning the sign bit
3024
// is preserved.
3125
a = INT128_C(-8);
3226
assert(int128_less(int128_shiftr(a, 1), INT128_C(0)));
3327

28+
// Right shift of more than 63 bits?
29+
assert(int128_eq(int128_shiftr((int128){ .high = 1 }, 64),
30+
INT128_C(1)));
31+
assert(int128_eq(int128_shiftr((int128){ .high = 0xfffffffffffffffe,
32+
.low = UINT64_MAX },
33+
64),
34+
INT128_C(-2)));
35+
assert(int128_eq(int128_shiftr((int128){ .high = 0xbfffffffffffffff,
36+
.low = UINT64_MAX },
37+
126),
38+
INT128_C(-2)));
39+
3440
// Negation Sanity Check
3541
assert(int128_eq(int128_neg(INT128_MAX),
3642
int128_add(INT128_MIN, INT128_C(1))));
@@ -77,6 +83,8 @@ void int128_tests()
7783
int128_mul(INT128_MAX, INT128_C(-1))));
7884
assert(int128_eq(int128_neg(INT128_MIN),
7985
int128_mul(INT128_MIN, INT128_C(-1))));
86+
assert(int128_eq(int128_neg(INT128_MIN),
87+
int128_mul(INT128_C(-1), INT128_MIN)));
8088

8189
// Fun fact: INT128_MAX (170141183460469231731687303715884105727) is
8290
// prime :)
@@ -86,15 +94,69 @@ void int128_tests()
8694
b = int128_div(a, INT128_C(2));
8795
assert(int128_eq(b, (int128){ .high = 100000000 }));
8896

89-
// Modulo Sanity Check
97+
// INT128_MIN divided by anything is 0, since INT128_MIN is less than
98+
// every other number...
99+
assert(int128_eq(int128_div(INT128_MIN, INT128_C(-123456789)),
100+
INT128_C(0)));
101+
102+
// Except for when dividing INT128_MIN by INT128_MIN, which should be
103+
// 1.
104+
assert(int128_eq(int128_div(INT128_MIN, INT128_MIN), INT128_C(1)));
105+
106+
// Check to make sure the sign is preserved properly:
107+
// - / - = +
108+
// - / + = -
109+
// + / - = -
110+
// + / + = +
111+
112+
a = INT128_C(1);
113+
b = INT128_C(-1);
114+
assert(int128_eq(int128_div(b, b), a));
115+
assert(int128_eq(int128_div(b, a), b));
116+
assert(int128_eq(int128_div(a, b), b));
117+
assert(int128_eq(int128_div(a, a), a));
118+
119+
// And of course anytime the denominator is greater than the numerator
120+
// the result is also 0.
121+
assert(int128_eq(int128_div(INT128_C(1), INT128_C(2)), INT128_C(0)));
122+
123+
// Remainder Sanity Check
90124
// 85070591730234615865843651857942052863 % 1234567890 = 1122498603
91125
a = int128_div(int128_sub(INT128_MAX, INT128_C(1)), INT128_C(2));
92126
b = INT128_C(1234567890);
93-
assert(int128_eq(int128_mod(a, b), INT128_C(1122498603)));
127+
assert(int128_eq(int128_rem(a, b), INT128_C(1122498603)));
128+
129+
a = int128_div(int128_sub(INT128_MAX, INT128_C(1)), INT128_C(2));
130+
b = INT128_C(-1234567890);
131+
132+
// The remainder operation has the same sign as the left hand side.
133+
assert(int128_eq(int128_rem(a, b), INT128_C(1122498603)));
134+
a = int128_neg(a);
135+
assert(int128_eq(int128_rem(a, b), INT128_C(-1122498603)));
136+
137+
// The remainder of anything divided by INT128_MIN is the left hand
138+
// side...
139+
assert(int128_eq(int128_rem(INT128_C(-1), INT128_MIN), INT128_C(-1)));
140+
141+
// Except for when the left hand side is INT128_MIN, in which case the
142+
// remainder is 0.
143+
assert(int128_eq(int128_rem(INT128_MIN, INT128_MIN), INT128_C(0)));
144+
145+
// This applies to all numbers.
146+
assert(int128_eq(int128_rem(INT128_C(1), INT128_C(1)), INT128_C(0)));
147+
148+
// When both the numerator and denominator are positive, if the
149+
// denominator is greater than the numerator the result is just the
150+
// numerator.
151+
assert(int128_eq(int128_rem(INT128_C(1), INT128_C(2)), INT128_C(1)));
94152
}
95153

96154
void uint128_tests()
97155
{
156+
// For most functions, the same logic is used for positive int128 and
157+
// uint128, so we only need to test things which we can't use int128 to
158+
// test, like large multiplication overflow.
159+
98160
// Multiplication Sanity Check
99161
uint128 a = (uint128){ .high = 100000000 };
100162
uint128 b = uint128_mul(a, UINT128_C(2));
@@ -122,6 +184,16 @@ void uint128_tests()
122184
};
123185

124186
assert(uint128_eq(b, correct));
187+
188+
// Right Shift Sanity Check
189+
// For unsigned integers, the most significant bit should not be
190+
// preserved.
191+
a = (uint128){ .high = UINT64_C(1) << 63 };
192+
assert(uint128_eq(uint128_shiftr(a, 1),
193+
(uint128){ .high = UINT64_C(1) << 62 }));
194+
195+
// uint128_less when high bits are equal?
196+
assert(uint128_less(UINT128_C(2), UINT128_C(5)));
125197
}
126198

127199
int main()

0 commit comments

Comments
 (0)