diff --git a/elastic/efloat/math/pow.cpp b/elastic/efloat/math/pow.cpp index c43cdcdc2..e3329ab7c 100644 --- a/elastic/efloat/math/pow.cpp +++ b/elastic/efloat/math/pow.cpp @@ -5,39 +5,31 @@ // // This file is part of the universal numbers project, which is released under an MIT Open Source license. #include -#include +#include #include -//#include -// generate specific test case that you can trace with the trace conditions in cfloat.h -// for most bugs they are traceable with _trace_conversion and _trace_add -template::type, Ty>::value -> -void GenerateTestCase(Ty fa, Ty fb) { - constexpr unsigned nbits = 16; - Ty fref; - sw::universal::bfloat16 a, b, ref, power; - a = fa; - b = fb; - fref = std::pow(fa, fb); - ref = fref; - power = sw::universal::pow(a, b); - std::cout << std::setprecision(nbits - 2); - std::cout << std::setw(nbits) << " -> pow(" << fa << "," << fb << ") = " << std::setw(nbits) << fref << std::endl; - std::cout << " -> pow( " << a << "," << b << ") = " << to_binary(power) << " (reference: " << to_binary(ref) << ") " ; - std::cout << (ref == power ? "PASS" : "FAIL") << std::endl << std::endl; - std::cout << std::setprecision(5); -} -#define MANUAL_TESTING 0 -#define STRESS_TESTING 0 +// Regression testing guards: typically set by the cmake configuration, but MANUAL_TESTING is an override +#define MANUAL_TESTING 1 +// REGRESSION_LEVEL_OVERRIDE is set by the cmake file to drive a specific regression intensity +// It is the responsibility of the regression test to organize the tests in a quartile progression. +//#undef REGRESSION_LEVEL_OVERRIDE +#ifndef REGRESSION_LEVEL_OVERRIDE +#undef REGRESSION_LEVEL_1 +#undef REGRESSION_LEVEL_2 +#undef REGRESSION_LEVEL_3 +#undef REGRESSION_LEVEL_4 +#define REGRESSION_LEVEL_1 1 +#define REGRESSION_LEVEL_2 1 +#define REGRESSION_LEVEL_3 1 +#define REGRESSION_LEVEL_4 1 +#endif int main() try { using namespace sw::universal; - std::string test_suite = "bfloat16 mathlib power function validation"; + std::string test_suite = "efloat mathlib power function validation"; std::string test_tag = "pow"; bool reportTestCases = false; int nrOfFailedTestCases = 0; @@ -45,36 +37,27 @@ try { ReportTestSuiteHeader(test_suite, reportTestCases); #if MANUAL_TESTING - // generate individual testcases to hand trace/debug - GenerateTestCase(4.0f, 2.0f); - - cout << endl; - //nrOfFailedTestCases += ReportTestResult(VerifyPowerFunction<16, 1>("Manual Testing", reportTestCases), "cfloat<16,1>", test_tag); + nrOfFailedTestCases = 1; ReportTestSuiteResults(test_suite, nrOfFailedTestCases); - return EXIT_SUCCESS; // ignore errors -#else + return EXIT_SUCCESS; // ignore failures +#else // !MANUAL_TESTING -#ifdef LATER - std::cout << "Integer power function\n"; - int a = 2; - unsigned b = 32; - std::cout << "2 ^ 32 = " << ipow(a, b) << '\n'; - std::cout << "2 ^ 32 = " << fastipow(a, uint8_t(b)) << '\n'; +#if REGRESSION_LEVEL_1 +#endif - int64_t c = 1024; - uint8_t d = 2; - std::cout << "1024 ^ 2 = " << ipow(c, d) << '\n'; - std::cout << "1M ^ 2 = " << ipow(ipow(c, d), d) << '\n'; +#if REGRESSION_LEVEL_2 +#endif - std::cout << "bfloat16 Power function validation\n"; - //nrOfFailedTestCases += ReportTestResult(VerifyPowerFunction< cfloat<8, 2, uint8_t> >(reportTestCases), "cfloat<8,2>", "pow"); -#endif // LATER +#if REGRESSION_LEVEL_3 +#endif + +#if REGRESSION_LEVEL_4 +#endif ReportTestSuiteResults(test_suite, nrOfFailedTestCases); return (nrOfFailedTestCases > 0 ? EXIT_FAILURE : EXIT_SUCCESS); - #endif // MANUAL_TESTING } catch (char const* msg) { diff --git a/include/universal/number/efloat/efloat_impl.hpp b/include/universal/number/efloat/efloat_impl.hpp index 4844f719f..5921366e0 100644 --- a/include/universal/number/efloat/efloat_impl.hpp +++ b/include/universal/number/efloat/efloat_impl.hpp @@ -1,7 +1,8 @@ #pragma once // efloat_impl.hpp: implementation of an adaptive precision binary floating-point number system // -// Copyright (C) 2017-2022 Stillwater Supercomputing, Inc. +// Copyright (C) 2017 Stillwater Supercomputing, Inc. +// SPDX-License-Identifier: MIT // // This file is part of the universal numbers project, which is released under an MIT Open Source license. #include @@ -12,6 +13,20 @@ #include #include +// supporting types and functions +#include // IEEE-754 decoders +#include + +/* +The efloat arithmetic can be configured to: +- throw exceptions on invalid arguments and operations +- return a signalling NaN + +Compile-time configuration flags are used to select the exception mode. +Run-time configuration is used to select modular vs saturation arithmetic. + +You need the exception types defined, but you have the option to throw them +*/ #include namespace sw { namespace universal { @@ -24,7 +39,7 @@ bool parse(const std::string& number, efloat& v); // efloat is an adaptive precision linear floating-point type class efloat { - using BlockType = uint32_t; + public: efloat() : sign(false), exp(0) { } @@ -35,43 +50,40 @@ class efloat { efloat& operator=(efloat&&) = default; // initializers for native types - constexpr efloat(signed char iv) noexcept { *this = iv; } - constexpr efloat(short iv) noexcept { *this = iv; } - constexpr efloat(int iv) noexcept { *this = iv; } - constexpr efloat(long iv) noexcept { *this = iv; } - constexpr efloat(long long iv) noexcept { *this = iv; } - constexpr efloat(char iv) noexcept { *this = iv; } - constexpr efloat(unsigned short iv) noexcept { *this = iv; } - constexpr efloat(unsigned int iv) noexcept { *this = iv; } - constexpr efloat(unsigned long iv) noexcept { *this = iv; } - constexpr efloat(unsigned long long iv) noexcept { *this = iv; } - constexpr efloat(float iv) noexcept { *this = iv; } - constexpr efloat(double iv) noexcept { *this = iv; } - + constexpr efloat(signed char iv) noexcept { *this = iv; } + constexpr efloat(short iv) noexcept { *this = iv; } + constexpr efloat(int iv) noexcept { *this = iv; } + constexpr efloat(long iv) noexcept { *this = iv; } + constexpr efloat(long long iv) noexcept { *this = iv; } + constexpr efloat(char iv) noexcept { *this = iv; } + constexpr efloat(unsigned short iv) noexcept { *this = iv; } + constexpr efloat(unsigned int iv) noexcept { *this = iv; } + constexpr efloat(unsigned long iv) noexcept { *this = iv; } + constexpr efloat(unsigned long long iv) noexcept { *this = iv; } + BIT_CAST_CONSTEXPR efloat(float iv) noexcept { *this = iv; } + BIT_CAST_CONSTEXPR efloat(double iv) noexcept { *this = iv; } // assignment operators for native types - constexpr efloat& operator=(signed char rhs) noexcept { return *this = convert(rhs); } - constexpr efloat& operator=(short rhs) noexcept { return *this = convert(rhs); } - constexpr efloat& operator=(int rhs) noexcept { return *this = convert(rhs); } - constexpr efloat& operator=(long rhs) noexcept { return *this = convert(rhs); } - constexpr efloat& operator=(long long rhs) noexcept { return *this = convert(rhs); } - constexpr efloat& operator=(char rhs) noexcept { return *this = convert(rhs); } - constexpr efloat& operator=(unsigned short rhs) noexcept { return *this = convert(rhs); } - constexpr efloat& operator=(unsigned int rhs) noexcept { return *this = convert(rhs); } - constexpr efloat& operator=(unsigned long rhs) noexcept { return *this = convert(rhs); } - constexpr efloat& operator=(unsigned long long rhs) noexcept { return *this = convert(rhs); } - constexpr efloat& operator=(float rhs) noexcept { return *this = convert(rhs); } - constexpr efloat& operator=(double rhs) noexcept { return *this = convert(rhs); } - constexpr efloat& operator=(long double rhs) noexcept { return *this = convert(rhs); } + constexpr efloat& operator=(signed char rhs) noexcept { return convert_signed(rhs); } + constexpr efloat& operator=(short rhs) noexcept { return convert_signed(rhs); } + constexpr efloat& operator=(int rhs) noexcept { return convert_signed(rhs); } + constexpr efloat& operator=(long rhs) noexcept { return convert_signed(rhs); } + constexpr efloat& operator=(long long rhs) noexcept { return convert_signed(rhs); } + constexpr efloat& operator=(char rhs) noexcept { return convert_unsigned(rhs); } + constexpr efloat& operator=(unsigned short rhs) noexcept { return convert_unsigned(rhs); } + constexpr efloat& operator=(unsigned int rhs) noexcept { return convert_unsigned(rhs); } + constexpr efloat& operator=(unsigned long rhs) noexcept { return convert_unsigned(rhs); } + constexpr efloat& operator=(unsigned long long rhs) noexcept { return convert_unsigned(rhs); } + BIT_CAST_CONSTEXPR efloat& operator=(float rhs) noexcept { return convert_ieee754(rhs); } + BIT_CAST_CONSTEXPR efloat& operator=(double rhs) noexcept { return convert_ieee754(rhs); } // conversion operators - explicit operator float() const noexcept { return convert_to_ieee754(); } - explicit operator double() const noexcept { return convert_to_ieee754(); } - explicit operator long double() const noexcept { return convert_to_ieee754(); } + explicit operator float() const noexcept { return convert_to_ieee754(); } + explicit operator double() const noexcept { return convert_to_ieee754(); } #if LONG_DOUBLE_SUPPORT - constexpr efloat(long double iv) noexcept : _bits{} { *this = iv; } - constexpr efloat& operator=(long double rhs) noexcept { return convert(rhs); } + constexpr efloat(long double iv) noexcept : _bits{} { *this = iv; } + BIT_CAST_CONSTEXPR efloat& operator=(long double rhs) noexcept { return convert_ieee754(rhs); } explicit operator long double() const noexcept { return convert_to_ieee754(); } #endif @@ -79,7 +91,7 @@ class efloat { efloat operator-() const { efloat negated(*this); return negated; -} + } // arithmetic operators efloat& operator+=(const efloat& rhs) { @@ -96,7 +108,7 @@ class efloat { } // modifiers - inline void clear() { sign = false; exp = 0; coef.clear(); } + inline void clear() { sign = false; exp = 0; limb.clear(); } inline void setzero() { clear(); } inline efloat& assign(const std::string& txt) { @@ -104,43 +116,55 @@ class efloat { } // selectors - inline bool iszero() const { return !sign && coef.size() == 0; } + inline bool iszero() const { return !sign && limb.size() == 0; } inline bool isone() const { return true; } inline bool isodd() const { return false; } inline bool iseven() const { return !isodd(); } inline bool ispos() const { return !sign; } inline bool ineg() const { return sign; } - inline int64_t scale() const { return exp + int64_t(coef.size()); } - - - void test(bool _sign, int _exp, std::vector& _coef) { - sign = _sign; - coef = _coef; - exp = _exp; - } + inline int64_t scale() const { return exp + int64_t(limb.size()); } protected: - bool sign; // sign of the number: -1 if true, +1 if false, zero is positive - int64_t exp; // exponent of the number - std::vector coef; // coefficients of the polynomial + bool sign; // sign of the number: -1 if true, +1 if false, zero is positive + int64_t exp; // exponent of the number + std::vector limb; // coefficients of the polynomial // HELPER methods // convert arithmetic types into an elastic floating-point - template - static constexpr efloat convert(Arith v) noexcept { - static_assert(std::is_arithmetic_v); - efloat f; - f.clear(); - if constexpr (std::is_integral_v && std::is_signed_v) { + template::value, SignedInt >::type> + constexpr efloat& convert_signed(SignedInt v) noexcept { + if (0 == v) { + setzero(); + } + else { + } - else if constexpr (std::is_unsigned_v) { + return *this; + } + + template::value, UnsignedInt >::type> + constexpr efloat& convert_unsigned(UnsignedInt v) noexcept { + if (0 == v) { + setzero(); } - else if constexpr (std::is_floating_point_v) { + else { + } - return f; + return *this; + } + + template::value, Real >::type> + constexpr efloat& convert_ieee754(Real rhs) noexcept { + + return *this; } + + // convert elastic floating-point to native ieee-754 template::value, Real >::type> constexpr Real convert_to_ieee754() const noexcept {