Skip to content

Commit

Permalink
update
Browse files Browse the repository at this point in the history
  • Loading branch information
lemire committed Feb 1, 2024
1 parent 2b2c062 commit f33f963
Show file tree
Hide file tree
Showing 6 changed files with 157 additions and 136 deletions.
5 changes: 2 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@ ifeq ($(DEBUG),1)
CFLAGS = -fPIC -std=c99 -ggdb -Wall -Wshadow -fsanitize=undefined -fno-omit-frame-pointer -fsanitize=address
CXXFLAGS = -fPIC -std=c++11 -ggdb -Wall -Wshadow -fsanitize=undefined -fno-omit-frame-pointer -fsanitize=address
else
CFLAGS = -fPIC -std=c99 -O3 -Wall -Wshadow
CXXFLAGS = -fPIC -O3 -Wall -Wshadow

CFLAGS = -fPIC -std=c99 -O3 -Wall -Wextra -Wshadow
CXXFLAGS = -fPIC -O3 -Wall -Wextra -Wshadow
endif # debug
all: unit cppincludetest2
HEADERS=include/fastmod.h
Expand Down
59 changes: 27 additions & 32 deletions include/fastmod.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,21 @@ namespace fastmod {

#ifdef _MSC_VER

// __umulh is only available in x64 mode under Visual Studio: don't compile to 32-bit!
// __umulh is only available in x64 mode under Visual Studio: don't compile to
// 32-bit!
FASTMOD_API uint64_t mul128_u32(uint64_t lowbits, uint32_t d) {
return __umulh(lowbits, d);
}
FASTMOD_API uint64_t mul128_from_u64(uint64_t lowbits, uint64_t d) {
return __umulh(lowbits, d);
}
FASTMOD_API uint64_t mul128_s32(uint64_t lowbits, int32_t d) {
if(d < 0) {
if (d < 0) {
return mul128_from_u64(lowbits, (int64_t)d) - lowbits;
}
return mul128_u32(lowbits, d);
}


#else // _MSC_VER NOT defined

FASTMOD_API uint64_t mul128_u32(uint64_t lowbits, uint32_t d) {
Expand All @@ -59,19 +59,22 @@ FASTMOD_API uint64_t mul128_from_u64(uint64_t lowbits, uint64_t d) {
return ((__uint128_t)lowbits * d) >> 64;
}
FASTMOD_API uint64_t mul128_s32(uint64_t lowbits, int32_t d) {
if(d < 0) {
if (d < 0) {
return mul128_from_u64(lowbits, (int64_t)d) - lowbits;
}
return mul128_u32(lowbits, d);
}

// This is for the 64-bit functions.
FASTMOD_API uint64_t mul128_u64(__uint128_t lowbits, uint64_t d) {
__uint128_t bottom_half = (lowbits & UINT64_C(0xFFFFFFFFFFFFFFFF)) * d; // Won't overflow
bottom_half >>= 64; // Only need the top 64 bits, as we'll shift the lower half away;
__uint128_t bottom_half =
(lowbits & UINT64_C(0xFFFFFFFFFFFFFFFF)) * d; // Won't overflow
bottom_half >>=
64; // Only need the top 64 bits, as we'll shift the lower half away;
__uint128_t top_half = (lowbits >> 64) * d;
__uint128_t both_halves = bottom_half + top_half; // Both halves are already shifted down by 64
both_halves >>= 64; // Get top half of both_halves
__uint128_t both_halves =
bottom_half + top_half; // Both halves are already shifted down by 64
both_halves >>= 64; // Get top half of both_halves
return (uint64_t)both_halves;
}

Expand All @@ -86,7 +89,6 @@ FASTMOD_API uint64_t mul128_u64(__uint128_t lowbits, uint64_t d) {
*
**/


// M = ceil( (1<<64) / d ), d > 0
FASTMOD_API uint64_t computeM_u32(uint32_t d) {
return UINT64_C(0xFFFFFFFFFFFFFFFF) / d + 1;
Expand All @@ -104,9 +106,7 @@ FASTMOD_API uint32_t fastdiv_u32(uint32_t a, uint64_t M) {
}

// given precomputed M, is_divisible checks whether n % d == 0
FASTMOD_API bool is_divisible(uint32_t n, uint64_t M) {
return n * M <= M - 1;
}
FASTMOD_API bool is_divisible(uint32_t n, uint64_t M) { return n * M <= M - 1; }

/**
* signed integers
Expand Down Expand Up @@ -189,34 +189,29 @@ FASTMOD_API uint64_t fastdiv_u64(uint64_t a, __uint128_t M) {

#ifdef __cplusplus

template<uint32_t d>
FASTMOD_API uint32_t fastmod(uint32_t x) {
FASTMOD_CONSTEXPR uint64_t v = computeM_u32(d);
return fastmod_u32(x, v, d);
template <uint32_t d> FASTMOD_API uint32_t fastmod(uint32_t x) {
FASTMOD_CONSTEXPR uint64_t v = computeM_u32(d);
return fastmod_u32(x, v, d);
}
template<uint32_t d>
FASTMOD_API uint32_t fastdiv(uint32_t x) {
FASTMOD_CONSTEXPR uint64_t v = computeM_u32(d);
return fastdiv_u32(x, v);
template <uint32_t d> FASTMOD_API uint32_t fastdiv(uint32_t x) {
FASTMOD_CONSTEXPR uint64_t v = computeM_u32(d);
return fastdiv_u32(x, v);
}
template<int32_t d>
FASTMOD_API int32_t fastmod(int32_t x) {
FASTMOD_CONSTEXPR uint64_t v = computeM_s32(d);
return fastmod_s32(x, v, d);
template <int32_t d> FASTMOD_API int32_t fastmod(int32_t x) {
FASTMOD_CONSTEXPR uint64_t v = computeM_s32(d);
return fastmod_s32(x, v, d);
}
template<int32_t d>
FASTMOD_API int32_t fastdiv(int32_t x) {
FASTMOD_CONSTEXPR uint64_t v = computeM_s32(d);
return fastdiv_s32(x, v, d);
template <int32_t d> FASTMOD_API int32_t fastdiv(int32_t x) {
FASTMOD_CONSTEXPR uint64_t v = computeM_s32(d);
return fastdiv_s32(x, v, d);
}

} // fastmod
#endif


// There's no reason to polute the global scope with this macro once its use ends
// This won't create any problems as the preprocessor will have done its thing once
// it reaches this point
// There's no reason to polute the global scope with this macro once its use
// ends This won't create any problems as the preprocessor will have done its
// thing once it reaches this point
#undef FASTMOD_API
#undef FASTMOD_CONSTEXPR

Expand Down
21 changes: 12 additions & 9 deletions tests/cppincludetest1.cpp
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
#include <assert.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <string.h>

#include "fastmod.h"

using namespace fastmod;


bool testunsigned64(uint64_t min, uint64_t max, bool verbose) {
for (uint64_t d = min; (d <= max) && (d >= min); d++) {
if (d == 0) {
Expand All @@ -23,15 +22,18 @@ bool testunsigned64(uint64_t min, uint64_t max, bool verbose) {
else
printf(".");
fflush(NULL);
for(uint64_t a64 = UINT64_C(0x10000000000000) /* 1 << 52 */; a64 < UINT64_C(0x10000000000000) + UINT64_C(0x10000); ++a64) {
for (uint64_t a64 = UINT64_C(0x10000000000000) /* 1 << 52 */;
a64 < UINT64_C(0x10000000000000) + UINT64_C(0x10000); ++a64) {
uint64_t computedFastMod = fastmod_u64(a64, M64, d);
uint64_t computedMod = a64 % d;
if (computedMod != computedFastMod) {
printf(
"(bad u64 fastmod) problem with divisor %" PRIu64 " and dividend %" PRIu64 " \n",
d, a64);
printf("expected %" PRIu64 " mod %" PRIu64 " = %" PRIu64 " \n", a64, d, computedMod);
printf("got %" PRIu64 " mod %" PRIu64 " = %" PRIu64 " \n", a64, d, computedFastMod);
printf("(bad u64 fastmod) problem with divisor %" PRIu64
" and dividend %" PRIu64 " \n",
d, a64);
printf("expected %" PRIu64 " mod %" PRIu64 " = %" PRIu64 " \n", a64, d,
computedMod);
printf("got %" PRIu64 " mod %" PRIu64 " = %" PRIu64 " \n", a64, d,
computedFastMod);
return false;
}
}
Expand All @@ -40,7 +42,8 @@ bool testunsigned64(uint64_t min, uint64_t max, bool verbose) {
printf("ok!\n");
}
if (verbose)
printf("Unsigned 64-bit fastmod test passed with divisors in interval [%" PRIu64 ", %" PRIu64 "].\n",
printf("Unsigned 64-bit fastmod test passed with divisors in interval "
"[%" PRIu64 ", %" PRIu64 "].\n",
min, max);
return true;
}
66 changes: 38 additions & 28 deletions tests/cppincludetest2.cpp
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
#include <assert.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <string.h>

#include "fastmod.h"

using namespace fastmod;


bool testunsigned64(uint64_t min, uint64_t max, bool verbose);



bool testunsigned(uint32_t min, uint32_t max, bool verbose) {
printf("Testing testunsigned with min = %d and max = %d\n", min, max);
printf("\n==Testing testunsigned with min = %d and max = %d\n", min, max);
size_t count = 0;
for (uint32_t d = min; (d <= max) && (d >= min); d++) {
if (d == 0) {
Expand All @@ -28,11 +25,11 @@ bool testunsigned(uint32_t min, uint32_t max, bool verbose) {
printf("d = %u (unsigned) ", d);
else {
count++;
if(count % 10000 == 0) {
if (count % 10000 == 0) {
printf(".");
fflush(NULL);
}
}
fflush(NULL);
for (uint64_t a64 = 0; a64 < UINT64_C(0x100000000); a64++) {
uint32_t a = (uint32_t)a64;
uint32_t computedFastMod = fastmod_u32(a, M, d);
Expand All @@ -55,6 +52,9 @@ bool testunsigned(uint32_t min, uint32_t max, bool verbose) {
}
if (verbose)
printf("ok!\n");
if (d == max) {
break;
}
}
if (verbose)
printf("Unsigned fastmod test passed with divisors in interval [%u, %u].\n",
Expand All @@ -63,7 +63,7 @@ bool testunsigned(uint32_t min, uint32_t max, bool verbose) {
}

bool testdivunsigned(uint32_t min, uint32_t max, bool verbose) {
printf("Testing testdivunsigned with min = %d and max = %d\n", min, max);
printf("\n==Testing testdivunsigned with min = %d and max = %d\n", min, max);
size_t count = 0;
for (uint32_t d = min; (d <= max) && (d >= min); d++) {
if (d == 0) {
Expand All @@ -79,11 +79,11 @@ bool testdivunsigned(uint32_t min, uint32_t max, bool verbose) {
printf("d = %u (unsigned) ", d);
else {
count++;
if(count % 10000 == 0) {
if (count % 10000 == 0) {
printf(".");
fflush(NULL);
}
}
fflush(NULL);
for (uint64_t a64 = 0; a64 < UINT64_C(0x100000000); a64++) {
uint32_t a = (uint32_t)a64;
uint32_t computedDiv = a / d;
Expand All @@ -100,6 +100,9 @@ bool testdivunsigned(uint32_t min, uint32_t max, bool verbose) {
}
if (verbose)
printf("ok!\n");
if (d == max) {
break;
}
}
if (verbose)
printf("Unsigned fastdiv test passed with divisors in interval [%u, %u].\n",
Expand All @@ -108,7 +111,7 @@ bool testdivunsigned(uint32_t min, uint32_t max, bool verbose) {
}

bool testsigned(int32_t min, int32_t max, bool verbose) {
printf("Testing testsigned with min = %d and max = %d\n", min, max);
printf("\n==Testing testsigned with min = %d and max = %d\n", min, max);
size_t count = 0;
assert(min != max + 1); // infinite loop!
for (int32_t d = min; (d <= max) && (d >= min); d++) {
Expand All @@ -125,11 +128,11 @@ bool testsigned(int32_t min, int32_t max, bool verbose) {
printf("d = %d (signed) ", d);
else {
count++;
if(count % 10000 == 0) {
if (count % 10000 == 0) {
printf(".");
fflush(NULL);
}
}
fflush(NULL);
int32_t positive_d = d < 0 ? -d : d;
uint64_t absolute_min32 = -INT64_C(0x80000000);
if (d == -1)
Expand All @@ -149,6 +152,9 @@ bool testsigned(int32_t min, int32_t max, bool verbose) {
}
if (verbose)
printf("ok!\n");
if (d == max) {
break;
}
}
if (verbose)
printf("Signed fastmod test passed with divisors in interval [%d, %d].\n",
Expand All @@ -157,9 +163,10 @@ bool testsigned(int32_t min, int32_t max, bool verbose) {
}

bool testdivsigned(int32_t min, int32_t max, bool verbose) {
printf("Testing divsigned with min = %d and max = %d\n", min, max);
printf("\n==Testing divsigned with min = %d and max = %d\n", min, max);
assert(min != max + 1); // infinite loop!
size_t count = 0;
static_assert(int32_t(-2147483648) < int32_t(2147483647));
for (int32_t d = min; (d <= max) && (d >= min); d++) {
if (d == 0) {
printf("skipping d = 0\n");
Expand All @@ -172,22 +179,22 @@ bool testdivsigned(int32_t min, int32_t max, bool verbose) {
if (d == -1) {
printf("skipping d = -1 as it is not supported\n");
continue;
}
}
if (d == -2147483648) {
printf("skipping d = -2147483648 as it is unsupported\n");
continue;
}

uint64_t M = computeM_s32(d);
if (verbose)
printf("d = %d (signed) ", d);
else {
if (verbose) {
printf("d = %d (signed)", d);
} else {
count++;
if(count % 10000 == 0) {
if (count % 10000 == 0) {
printf(".");
fflush(NULL);
}
}
fflush(NULL);
for (int64_t a64 = -INT64_C(0x80000000); a64 < INT64_C(0x80000000); a64++) {
int32_t a = (int32_t)a64;
int32_t computedDiv = a / d;
Expand All @@ -198,7 +205,7 @@ bool testdivsigned(int32_t min, int32_t max, bool verbose) {
d, a);
printf("expected %d div %d = %d \n", a, d, computedDiv);
printf("got %d div %d = %d \n", a, d, computedFastDiv);
if(d == -2147483648) {
if (d == -2147483648) {
printf("Note: d = -2147483648 is unsupported\n");
} else {
return false;
Expand All @@ -207,31 +214,34 @@ bool testdivsigned(int32_t min, int32_t max, bool verbose) {
}
if (verbose)
printf("ok!\n");
if (d == max) {
break;
}
}
if (verbose)
printf("Signed fastdiv test passed with divisors in interval [%d, %d].\n",
min, max);
return true;
}



int main(int argc, char *argv[]) {
bool isok = true;
bool verbose = false;
for(int i = 0; i < argc; ++i) {
if(strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {
verbose = true; break;
for (int i = 0; i < argc; ++i) {
if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {
verbose = true;
break;
}
}

isok = isok && testdivsigned(0x7ffffff8, 0x7fffffff, verbose);
isok = isok && testdivsigned(-0x80000000, -0x7ffffff8, verbose);
isok = isok && testdivsigned(2, 10, verbose);
isok = isok && testdivsigned(0x7ffffff8, 0x7fffffff, verbose);
isok = isok && testdivsigned(-10, -2, verbose);
isok = isok && testunsigned64(1, 0x10, verbose);
isok = isok && testunsigned64(0x000133F, 0xFFFF, verbose);
isok = isok && testunsigned64(UINT64_C(0xffffffffff00000), UINT64_C(0xffffffffff00000) + 0x100, verbose);
isok = isok && testunsigned64(UINT64_C(0xffffffffff00000),
UINT64_C(0xffffffffff00000) + 0x100, verbose);
isok = isok && testunsigned(1, 8, verbose);
isok = isok && testunsigned(0xfffffff8, 0xffffffff, verbose);

Expand Down
Loading

0 comments on commit f33f963

Please sign in to comment.