Skip to content

Commit

Permalink
WIP: simplifying the convertion API by removing the constexpr type de…
Browse files Browse the repository at this point in the history
…pendencies
  • Loading branch information
Ravenwater committed Sep 16, 2024
1 parent d6f1ea9 commit 20ee257
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 102 deletions.
75 changes: 29 additions & 46 deletions elastic/efloat/math/pow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,76 +5,59 @@
//
// This file is part of the universal numbers project, which is released under an MIT Open Source license.
#include <universal/utility/directives.hpp>
#include <universal/number/bfloat/bfloat.hpp>
#include <universal/number/efloat/efloat.hpp>
#include <universal/verification/test_suite.hpp>
//#include <universal/verification/bfloat_math_test_suite.hpp>

// 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<typename Ty,
typename = typename std::enable_if<std::is_floating_point<Ty>::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;

ReportTestSuiteHeader(test_suite, reportTestCases);

#if MANUAL_TESTING
// generate individual testcases to hand trace/debug
GenerateTestCase<float>(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) {
Expand Down
136 changes: 80 additions & 56 deletions include/universal/number/efloat/efloat_impl.hpp
Original file line number Diff line number Diff line change
@@ -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 <string>
Expand All @@ -12,6 +13,20 @@
#include <vector>
#include <map>

// supporting types and functions
#include <universal/native/ieee754.hpp> // IEEE-754 decoders
#include <universal/number/shared/specific_value_encoding.hpp>

/*
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 <universal/number/efloat/exceptions.hpp>

namespace sw { namespace universal {
Expand All @@ -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) { }

Expand All @@ -35,51 +50,48 @@ 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<float>(); }
explicit operator double() const noexcept { return convert_to_ieee754<double>(); }
explicit operator long double() const noexcept { return convert_to_ieee754<long double>(); }
explicit operator float() const noexcept { return convert_to_ieee754<float>(); }
explicit operator double() const noexcept { return convert_to_ieee754<double>(); }

#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<long double>(); }
#endif

// prefix operators
efloat operator-() const {
efloat negated(*this);
return negated;
}
}

// arithmetic operators
efloat& operator+=(const efloat& rhs) {
Expand All @@ -96,51 +108,63 @@ 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) {
return *this;
}

// 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<BlockType>& _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<BlockType> 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<double> limb; // coefficients of the polynomial

// HELPER methods

// convert arithmetic types into an elastic floating-point
template<typename Arith>
static constexpr efloat convert(Arith v) noexcept {
static_assert(std::is_arithmetic_v<Arith>);
efloat f;
f.clear();
if constexpr (std::is_integral_v<Arith> && std::is_signed_v<Arith>) {
template<typename SignedInt,
typename = typename std::enable_if< std::is_integral<SignedInt>::value, SignedInt >::type>
constexpr efloat& convert_signed(SignedInt v) noexcept {
if (0 == v) {
setzero();
}
else {

}
else if constexpr (std::is_unsigned_v<Arith>) {
return *this;
}

template<typename UnsignedInt,
typename = typename std::enable_if< std::is_integral<UnsignedInt>::value, UnsignedInt >::type>
constexpr efloat& convert_unsigned(UnsignedInt v) noexcept {
if (0 == v) {
setzero();
}
else if constexpr (std::is_floating_point_v<Arith>) {
else {

}
return f;
return *this;
}

template<typename Real,
typename = typename std::enable_if< std::is_floating_point<Real>::value, Real >::type>
constexpr efloat& convert_ieee754(Real rhs) noexcept {

return *this;
}


// convert elastic floating-point to native ieee-754
template<typename Real,
typename = typename std::enable_if< std::is_floating_point<Real>::value, Real >::type>
constexpr Real convert_to_ieee754() const noexcept {
Expand Down

0 comments on commit 20ee257

Please sign in to comment.