From a63b5390ad6d53b3fdba9855e5dff3329a08faf4 Mon Sep 17 00:00:00 2001 From: Naoki Shibata Date: Thu, 23 May 2024 23:17:49 +0900 Subject: [PATCH] Add more testing and documentation --- doxygen.conf.in | 3 +- src/include/testerutil.hpp | 66 ++++++++++++++++++++++++++++++++-- src/include/tlfloat/bigint.hpp | 16 ++++++++- src/include/tlfloat/tlfloat.h | 56 +++++++++++++++++++++-------- src/tester/test_arith.cpp | 38 -------------------- src/tester/test_bigint3.cpp | 2 +- src/tester/test_bigint4.cpp | 18 ++++++---- src/tester/test_printf.cpp | 29 +++++++++++++++ 8 files changed, 163 insertions(+), 65 deletions(-) diff --git a/doxygen.conf.in b/doxygen.conf.in index 26078be..6b8cfa5 100644 --- a/doxygen.conf.in +++ b/doxygen.conf.in @@ -114,7 +114,8 @@ EXCLUDE_PATTERNS = test* */util/* auxiliary.hpp suppress.hpp tlfloatutil.h EXCLUDE_SYMBOLS = *::detail yarray LCG64 RNG PSHA2_256_Internal CryptUtil tlfloat_bigint* \ BigUInt*<*6*> *toHexString to_string_d \ TLFloat*::getUnpacked detail::UnpackedFloat \ - TLFLOAT_OVERLOAD_OP2 TLFLOAT_OVERLOAD_CMP + TLFLOAT_OVERLOAD_OP2 TLFLOAT_OVERLOAD_CMP TLFLOAT_OVERLOAD_CMPQ \ + TLFLOAT_OVERLOAD_OP2I TLFLOAT_OVERLOAD_CMPI TLFLOAT_OVERLOAD_OP2Q EXAMPLE_PATH = EXAMPLE_PATTERNS = * EXAMPLE_RECURSIVE = NO diff --git a/src/include/testerutil.hpp b/src/include/testerutil.hpp index a87e274..e10f5db 100644 --- a/src/include/testerutil.hpp +++ b/src/include/testerutil.hpp @@ -695,6 +695,18 @@ float rndf(shared_ptr rng) { } } +bool cmpf(float x, float y, int t=0) { + if (isnan(x) && isnan(y)) return true; + uint32_t u, v; + memcpy((void *)&u, (void *)&x, sizeof(u)); + memcpy((void *)&v, (void *)&y, sizeof(v)); + + if (t == 0) return u == v; + + int d = int(int32_t(u) - int32_t(v)); + return -t <= d && d <= t; +} + double rndd(shared_ptr rng) { uint64_t r = rng->nextLT(1000); @@ -728,8 +740,20 @@ double rndd(shared_ptr rng) { } } +bool cmpd(double x, double y, int t=0) { + if (isnan(x) && isnan(y)) return true; + uint64_t u, v; + memcpy((void *)&u, (void *)&x, sizeof(u)); + memcpy((void *)&v, (void *)&y, sizeof(v)); + + if (t == 0) return u == v; + + int d = int(int64_t(u) - int64_t(v)); + return -t <= d && d <= t; +} + #ifdef __TLFLOAT_HPP_INCLUDED__ -Quad rndq_(shared_ptr rng) { +Quad rndQ(shared_ptr rng) { uint64_t r = rng->nextLT(1000); if (r == 0) { @@ -769,13 +793,37 @@ Quad rndq_(shared_ptr rng) { } #ifdef ENABLE_QUAD -quad rndq(shared_ptr rng) { return (quad)rndq_(rng); } +quad rndq(shared_ptr rng) { return (quad)rndQ(rng); } typedef quad quad_; + +bool cmpq(quad x, quad y, int t=0) { + if (isnanq(x) && isnanq(y)) return true; + BigUInt<7> u, v; + memcpy((void *)&u, (void *)&x, sizeof(u)); + memcpy((void *)&v, (void *)&y, sizeof(v)); + + if (t == 0) return u == v; + + int d = int(BigInt<7>(u) - BigInt<7>(v)); + return -t <= d && d <= t; +} #else -Quad rndq(shared_ptr rng) { return rndq_(rng); } typedef Quad quad_; +quad_ rndq(shared_ptr rng) { return rndQ(rng); } #endif +bool cmpq(Quad x, Quad y, int t=0) { + if (isnan(x) && isnan(y)) return true; + BigUInt<7> u, v; + memcpy((void *)&u, (void *)&x, sizeof(u)); + memcpy((void *)&v, (void *)&y, sizeof(v)); + + if (t == 0) return u == v; + + int d = int(BigInt<7>(u) - BigInt<7>(v)); + return -t <= d && d <= t; +} + Octuple rndo(shared_ptr rng) { uint64_t r = rng->nextLT(1000); @@ -814,6 +862,18 @@ Octuple rndo(shared_ptr rng) { } } } + +bool cmpo(Octuple x, Octuple y, int t=0) { + if (isnan(x) && isnan(y)) return true; + BigUInt<8> u, v; + memcpy((void *)&u, (void *)&x, sizeof(u)); + memcpy((void *)&v, (void *)&y, sizeof(v)); + + if (t == 0) return u == v; + + int d = int(BigInt<8>(u) - BigInt<8>(v)); + return -t <= d && d <= t; +} #endif // #ifdef __TLFLOAT_HPP_INCLUDED__ template diff --git a/src/include/tlfloat/bigint.hpp b/src/include/tlfloat/bigint.hpp index d96811c..76c1640 100644 --- a/src/include/tlfloat/bigint.hpp +++ b/src/include/tlfloat/bigint.hpp @@ -769,8 +769,22 @@ namespace tlfloat { // - constexpr BigUInt(const char *p, const char **endptr = nullptr, const int base = 10) { + constexpr BigUInt(const char *p, const char **endptr = nullptr, const int base_ = 10) { while(*p == ' ') p++; + int base = base_; + if (base_ == 0) { + if (*p == '0') { + if (*(p+1) == 'x') { + base = 16; + p += 2; + } else { + base = 8; + p++; + } + } else base = 10; + } + if (base < 2 || base > 36) { low = high = 0; return; } + BigUInt r; uint64_t u = 0, d = 1; while(*p != '\0') { diff --git a/src/include/tlfloat/tlfloat.h b/src/include/tlfloat/tlfloat.h index da6efee..d5fc922 100644 --- a/src/include/tlfloat/tlfloat.h +++ b/src/include/tlfloat/tlfloat.h @@ -29,6 +29,12 @@ extern "C" { typedef __int128_t tlfloat_int128_t_; typedef __uint128_t tlfloat_uint128_t_; #else +/** tlfloat_int128_t_ and tlfloat_uint128_t_ are types for handling + * 128-bit integer numbers in C. The data size and data structure of + * these types are the same as ordinary integer types. These are POD + * types that can be safely passed with C ABI. If the compiler + * supports __int128_t and __uint128_t, these type are aliases for + * those types. */ typedef struct { uint64_t e[1 << 1]; } tlfloat_int128_t_; typedef struct { uint64_t e[1 << 1]; } tlfloat_uint128_t_; #endif @@ -41,7 +47,7 @@ extern "C" { /** This type is for handling quadruple-precision IEEE floating-point * numbers in C. The data size and data structure of this type are the * same as a quad-precision floating-point number. This is a POD type - * that can be safely passed with C ABI. If the comipler supports + * that can be safely passed with C ABI. If the compiler supports * __float128, this type is an alias for __float128. When compling * with a compiler on which long double is IEEE float 128, this type * is an alias for long double. */ @@ -592,13 +598,13 @@ extern "C" { #endif #if (defined(__cplusplus) && !defined(TLFLOAT_COMPILER_SUPPORTS_FLOAT128) && !defined(TLFLOAT_LONGDOUBLE_IS_FLOAT128)) || defined(TLFLOAT_DOXYGEN) -/** This type is for handling quadruple-precision IEEE floating-point - * numbers in C and C++11. The data size and data structure of this - * type are the same as a quad-precision floating-point number. When - * compling C code, this type is an alias for tlfloat_quad_. When - * compling C++ code without IEEE float 128 support, this is a struct - * encapsulating a tlfloat_quad_ variable with operators - * overloaded. */ +/** tlfloat_quad is a trivially copyable type for handling + * quadruple-precision IEEE floating-point numbers in C and C++11. The + * data size and data structure of this type are the same as a + * quad-precision floating-point number. When compling C code, this + * type is an alias for tlfloat_quad_. When compling C++ code without + * IEEE float 128 support, this is a struct encapsulating a + * tlfloat_quad_ variable with operators overloaded. */ struct tlfloat_quad { tlfloat_quad_ value; @@ -643,19 +649,22 @@ struct tlfloat_quad { bool operator> (const tlfloat_quad& rhs) const { return tlfloat_gt_q_q(value, rhs.value); } bool operator>=(const tlfloat_quad& rhs) const { return tlfloat_ge_q_q(value, rhs.value); } }; + +/** This macro is defined iff tlfloat_quad is not an alias of + __float128, but a struct defined in tlfloat.h. */ #define TLFLOAT_QUAD_IS_STRUCT #else // #if (defined(__cplusplus) && !defined(TLFLOAT_COMPILER_SUPPORTS_FLOAT128) && !defined(TLFLOAT_LONGDOUBLE_IS_FLOAT128)) || defined(TLFLOAT_DOXYGEN) typedef tlfloat_quad_ tlfloat_quad; #endif #if defined(__cplusplus) || defined(TLFLOAT_DOXYGEN) -/** This type is for handling octuple-precision IEEE floating-point - * numbers in C and older C++11. The data size and data structure of - * this type are the same as an octuple-precision floating-point - * number. When compling C code, this type is an alias for - * tlfloat_octuple_. When compling C++ code, this is a struct - * encapsulating a tlfloat_octuple_ variable with operators - * overloaded. */ +/** tlfloat_octuple is a trivially copyable type for handling + * octuple-precision IEEE floating-point numbers in C and older + * C++11. The data size and data structure of this type are the same + * as an octuple-precision floating-point number. When compling C + * code, this type is an alias for tlfloat_octuple_. When compling C++ + * code, this is a struct encapsulating a tlfloat_octuple_ variable + * with operators overloaded. */ struct tlfloat_octuple { tlfloat_octuple_ value; @@ -706,6 +715,12 @@ typedef tlfloat_octuple_ tlfloat_octuple; #endif #if (defined(__cplusplus) && !defined(TLFLOAT_COMPILER_SUPPORTS_INT128)) || defined(TLFLOAT_DOXYGEN) +/** tlfloat_int128_t is a trivially copyable type for handling 128-bit + * signed integer in C and C++11. The data size and data structure of + * this type are the same as ordinary integer types. When compling C + * code, this type is an alias for tlfloat_int128_t_. When compling + * C++ code without __int128_t support, this is a struct encapsulating + * a tlfloat_int128_t variable with operators overloaded. */ struct tlfloat_int128_t { tlfloat_int128_t_ value; @@ -779,6 +794,13 @@ struct tlfloat_int128_t { tlfloat_int128_t operator--(int) { tlfloat_int128_t t = *this; *this = tlfloat_sub_i128_i128(value, tlfloat_cast_i128_i64(1)); return t; } }; +/** tlfloat_uint128_t is a trivially copyable type for handling + * 128-bit unsigned integer in C and C++11. The data size and data + * structure of this type are the same as ordinary integer types. When + * compling C code, this type is an alias for tlfloat_uint128_t_. When + * compling C++ code without __uint128_t support, this is a struct + * encapsulating a tlfloat_int128_t variable with operators + * overloaded. */ struct tlfloat_uint128_t { tlfloat_uint128_t_ value; @@ -852,6 +874,10 @@ struct tlfloat_uint128_t { inline tlfloat_int128_t::tlfloat_int128_t(const tlfloat_uint128_t_& d) { memcpy((void *)this, (void *)&d, sizeof(*this)); } inline tlfloat_int128_t::operator tlfloat_uint128_t_() const { tlfloat_uint128_t v; memcpy((void *)&v, (void *)this, sizeof(v)); return v; } + +/** This macro is defined iff tlfloat_int128_t and tlfloat_uint128_t + are not aliases of __int128_t and __uint128_t, but structs defined + in tlfloat.h. */ #define TLFLOAT_INT128_IS_STRUCT #else typedef tlfloat_int128_t_ tlfloat_int128_t; diff --git a/src/tester/test_arith.cpp b/src/tester/test_arith.cpp index e089c2c..8828e0d 100644 --- a/src/tester/test_arith.cpp +++ b/src/tester/test_arith.cpp @@ -22,44 +22,6 @@ static_assert(is_trivially_copyable_v == true); static_assert(is_trivially_copyable_v == true); static_assert(is_trivially_copyable_v == true); -static bool cmpf(float x, float y, int t=0) { - if (isnan(x) && isnan(y)) return true; - uint32_t u, v; - memcpy((void *)&u, (void *)&x, sizeof(u)); - memcpy((void *)&v, (void *)&y, sizeof(v)); - - if (t == 0) return u == v; - - int d = int(int32_t(u) - int32_t(v)); - return -t <= d && d <= t; -} - -static bool cmpd(double x, double y, int t=0) { - if (isnan(x) && isnan(y)) return true; - uint64_t u, v; - memcpy((void *)&u, (void *)&x, sizeof(u)); - memcpy((void *)&v, (void *)&y, sizeof(v)); - - if (t == 0) return u == v; - - int d = int(int64_t(u) - int64_t(v)); - return -t <= d && d <= t; -} - -#ifdef ENABLE_QUAD -static bool cmpq(quad x, quad y, int t=0) { - if (isnanq(x) && isnanq(y)) return true; - BigUInt<7> u, v; - memcpy((void *)&u, (void *)&x, sizeof(u)); - memcpy((void *)&v, (void *)&y, sizeof(v)); - - if (t == 0) return u == v; - - int d = int(BigInt<7>(u) - BigInt<7>(v)); - return -t <= d && d <= t; -} -#endif - int main(int argc, char **argv) { uint64_t niter = 100000; diff --git a/src/tester/test_bigint3.cpp b/src/tester/test_bigint3.cpp index c92092f..3d0e0c5 100644 --- a/src/tester/test_bigint3.cpp +++ b/src/tester/test_bigint3.cpp @@ -139,7 +139,7 @@ void checks(uint64_t high0, uint64_t low0, uint64_t high1, uint64_t low1, double if (!equal(i0 += 3, b0 += 3)) e("+="); if (!equal(i0 -= 3, b0 -= 3)) e("-="); if (!equal(i0 *= 3, b0 *= 3)) e("*="); - //if (!equal(i0 /= 3, b0 /= 3)) e("/="); + if (!equal(i0 /= 3, b0 /= 3)) e("/="); if (!equal(i1 += i0, b1 += b0)) e("+="); if (!equal(i1 -= i0, b1 -= b0)) e("-="); diff --git a/src/tester/test_bigint4.cpp b/src/tester/test_bigint4.cpp index 953a8c9..cb0f037 100644 --- a/src/tester/test_bigint4.cpp +++ b/src/tester/test_bigint4.cpp @@ -86,7 +86,8 @@ void checks(uint64_t high0, uint64_t low0, uint64_t high1, uint64_t low1, double { char str[256]; tlfloat_snprintf(str, sizeof(str), "%Qd", b0); - if (b0 != tlfloat_strtoi128(str, NULL, 10)) e("to/from string signed base 10"); + if (b0 != tlfloat_strtoi128(str, NULL, 10)) e("Qd to/from string signed base 10"); + if (b0 != tlfloat_strtoi128(str, NULL, 0)) e("Qd to/from string signed base 0"); } if ((i0 == i1) != (b0 == b1)) e("=="); @@ -158,12 +159,12 @@ void checks(uint64_t high0, uint64_t low0, uint64_t high1, uint64_t low1, double if (!equal(i0 += 3, b0 += 3)) e("+="); if (!equal(i0 -= 3ULL, b0 -= 3ULL)) e("-="); if (!equal(i0 *= 3, b0 *= 3)) e("*="); - //if (!equal(i0 /= 3, b0 /= 3)) e("/="); + if (!equal(i0 /= 3, b0 /= 3)) e("/="); if (!equal(i1 += i0, b1 += b0)) e("+="); if (!equal(i1 -= i0, b1 -= tlfloat_int128_t_(b0))) e("-="); if (!equal(i1 *= i0, b1 *= tlfloat_uint128_t(b0))) e("*="); - //if (!equal(i1 /= i0, b1 /= b0)) e("/="); + if (!equal(i1 /= i0, b1 /= b0)) e("/="); } void checku(uint64_t high0, uint64_t low0, uint64_t high1, uint64_t low1, double d) { @@ -223,13 +224,18 @@ void checku(uint64_t high0, uint64_t low0, uint64_t high1, uint64_t low1, double char str[256]; tlfloat_snprintf(str, sizeof(str), "%Qu", b0); - if (b0 != tlfloat_strtou128(str, NULL, 10)) e("to/from string unsigned base 10"); + if (b0 != tlfloat_strtou128(str, NULL, 10)) e("Qu to/from string unsigned base 10"); + if (b0 != tlfloat_strtou128(str, NULL, 0)) e("Qu to/from string unsigned base 10"); tlfloat_snprintf(str, sizeof(str), "%Qo", b0); - if (b0 != tlfloat_strtou128(str, NULL, 8)) e("to/from string unsigned base 8"); + if (b0 != tlfloat_strtou128(str, NULL, 8)) e("Qo to/from string unsigned base 8"); + tlfloat_snprintf(str, sizeof(str), "0%Qo", b0); + if (b0 != tlfloat_strtou128(str, NULL, 0)) e("Qo to/from string unsigned base 0"); tlfloat_snprintf(str, sizeof(str), "%Qx", b0); - if (b0 != tlfloat_strtou128(str, NULL, 16)) e("to/from string unsigned base 16"); + if (b0 != tlfloat_strtou128(str, NULL, 16)) e("Qx to/from string unsigned base 16"); + tlfloat_snprintf(str, sizeof(str), "0x%Qx", b0); + if (b0 != tlfloat_strtou128(str, NULL, 0)) e("Qx to/from string unsigned base 0"); } if ((i0 == i1) != (b0 == b1)) e("=="); diff --git a/src/tester/test_printf.cpp b/src/tester/test_printf.cpp index 4071b34..3be9ff8 100644 --- a/src/tester/test_printf.cpp +++ b/src/tester/test_printf.cpp @@ -8,6 +8,8 @@ #include "tlfloat/tlfloat.hpp" #include "tlfloat/tlfloat.h" +#include "testerutil.hpp" + using namespace tlfloat; static const int test_widths[] = { -1, 0, 1, 2, 3, 4, 6, 8, 12, 16, 20, 32 }; @@ -772,6 +774,7 @@ void testem128(int64_t val) { using namespace std; int main(int argc, char **argv) { + auto rng = createPreferredRNG(); int var; doTest("head %d [%*g] [%.*g] [%*.*g] %d tail", 123, 8, 100.1234567, 7, 101.1234567, 11, 9, 102.1234567, 321); doTest("head %d [%*d] [%.*d] [%*.*d] %d tail", 123, 8, 100, 7, 101, 11, 9, 102, 321); @@ -891,6 +894,19 @@ int main(int argc, char **argv) { testem(+1.234567890123456789e-120); testem(-1.234567890123456789e-120); + for(int i=0;i<1000;i++) { + static char str[1024]; + Quad qc = rndQ(rng); + tlfloat_snprintf(str, sizeof(str), "%.40_128g", qc); + Quad qt = tlfloat_strtoq(str, nullptr); + if (!cmpq(qc, qt, 1)) { + printf("Quad snprintf : %s\n", str); + tlfloat_printf("qc = %.40_128g\n", qc); + tlfloat_printf("qt = %.40_128g\n", qt); + exit(-1); + } + } + cout << "Quad OK" << endl; testem(NAN); @@ -919,6 +935,19 @@ int main(int argc, char **argv) { testem(+1.234567890123456789e-120); testem(-1.234567890123456789e-120); + for(int i=0;i<1000;i++) { + static char str[1024]; + Octuple oc = rndo(rng); + tlfloat_snprintf(str, sizeof(str), "%.80_256g", oc); + Octuple ot = tlfloat_strtoo(str, nullptr); + if (!cmpo(oc, ot, 1)) { + printf("Octuple snprintf : %s\n", str); + tlfloat_printf("oc = %.80_256g\n", oc); + tlfloat_printf("ot = %.80_256g\n", ot); + exit(-1); + } + } + cout << "Octuple OK" << endl; cout << "OK" << endl;