Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ option(UNIVERSAL_BUILD_NUMBER_EDECIMALS "Set to ON to build elastic e
option(UNIVERSAL_BUILD_NUMBER_ERATIONALS "Set to ON to build elastic erational tests" OFF)
option(UNIVERSAL_BUILD_NUMBER_EFLOATS "Set to ON to build elastic efloat tests" OFF)
option(UNIVERSAL_BUILD_NUMBER_EREALS "Set to ON to build elastic ereals tests" OFF)
option(UNIVERSAL_BUILD_NUMBER_ELREALS "Set to ON to build elastic elreal tests" OFF)

option(UNIVERSAL_BUILD_NUMBER_STATICS "Set to ON to build static arithmetic type tests" OFF)
option(UNIVERSAL_BUILD_NUMBER_INTEGERS "Set to ON to build static integer tests" OFF)
Expand Down Expand Up @@ -893,6 +894,9 @@ if(UNIVERSAL_BUILD_CI_LITE)
# one elastic type
set(UNIVERSAL_BUILD_NUMBER_EINTEGERS ON)

# elreal Phase 1 (block<FpType>): portability across platforms is critical
set(UNIVERSAL_BUILD_NUMBER_ELREALS ON)

# conversions
set(UNIVERSAL_BUILD_NUMBER_CONVERSIONS ON)

Expand Down Expand Up @@ -944,6 +948,7 @@ if(UNIVERSAL_BUILD_NUMBER_ELASTICS)
set(UNIVERSAL_BUILD_NUMBER_ERATIONALS ON)
set(UNIVERSAL_BUILD_NUMBER_EFLOATS ON)
set(UNIVERSAL_BUILD_NUMBER_EREALS ON)
set(UNIVERSAL_BUILD_NUMBER_ELREALS ON)
endif(UNIVERSAL_BUILD_NUMBER_ELASTICS)

if(UNIVERSAL_BUILD_NUMBER_STATICS)
Expand Down Expand Up @@ -1077,6 +1082,10 @@ if(UNIVERSAL_BUILD_NUMBER_EREALS)
add_subdirectory("elastic/ereal")
endif(UNIVERSAL_BUILD_NUMBER_EREALS)

if(UNIVERSAL_BUILD_NUMBER_ELREALS)
add_subdirectory("elastic/elreal")
endif(UNIVERSAL_BUILD_NUMBER_ELREALS)


###################################################################
### fixed-size number system tests
Expand Down
206 changes: 206 additions & 0 deletions docs/bugs/elreal-implementation.md

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions elastic/elreal/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# elastic/elreal/CMakeLists.txt
#
# Phase 1 (#925): block<FpType> representation only. Co-list, arithmetic,
# math suite and conversion are added in subsequent phases (#926-#933).

file (GLOB BLOCK_SRCS "./block/*.cpp")

compile_all("true" "el_block" "Number Systems/elastic/floating-point/binary/elreal/block" "${BLOCK_SRCS}")
86 changes: 86 additions & 0 deletions elastic/elreal/block/accessors.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// accessors.cpp: sign(), scale_of_v(), exponent(), value_as<>() tests for
// elreal block<FpType>.
//
// 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 <universal/utility/directives.hpp>
#include <cmath>
#include <cstdint>
#include <iostream>

#include <universal/number/cfloat/cfloat.hpp>
#include <universal/number/bfloat16/bfloat16.hpp>
#include <universal/number/elreal/elreal.hpp>
#include <universal/verification/test_suite.hpp>

namespace {

template <typename FpType>
int verify_accessors(const std::string& tag) {
using namespace sw::universal;
using B = block<FpType>;
int nrFailures = 0;

// sign on positive, negative, +0, -0
B pos{ FpType{1.5}, 0 };
B neg{ FpType{-1.5}, 0 };
if (pos.sign() != +1) { std::cout << tag << " pos.sign() != +1\n"; ++nrFailures; }
if (neg.sign() != -1) { std::cout << tag << " neg.sign() != -1\n"; ++nrFailures; }

// scale_of_v: powers of two yield integer scales
B p2_0{ FpType{1}, 0 }; // 2^0
B p2_1{ FpType{2}, 0 }; // 2^1
B p2_n1{ FpType{0.5}, 0 }; // 2^-1
if (p2_0.scale_of_v() != 0) { std::cout << tag << " scale(1.0) != 0\n"; ++nrFailures; }
if (p2_1.scale_of_v() != 1) { std::cout << tag << " scale(2.0) != 1\n"; ++nrFailures; }
if (p2_n1.scale_of_v() != -1) { std::cout << tag << " scale(0.5) != -1\n"; ++nrFailures; }

// exponent() = scale_of_v + exp_offset * 2^E
B off2{ FpType{1.5}, 2 };
std::int64_t expected = off2.scale_of_v() + std::int64_t(2) * B::exp_step;
if (off2.exponent() != expected) {
std::cout << tag << " exponent() with offset wrong: got " << off2.exponent()
<< " expected " << expected << '\n';
++nrFailures;
}

// value_as<double> with offset=0: the recovered value must bit-match the
// host FpType's stored value (no rounding -- value_as is just a cast).
B v{ FpType{3.25}, 0 };
double recovered = v.template value_as<double>();
double host_stored = static_cast<double>(FpType{3.25});
if (recovered != host_stored) {
std::cout << tag << " value_as<double>(off=0) mismatch: "
<< recovered << " vs host_stored " << host_stored << '\n';
++nrFailures;
}

return nrFailures;
}

} // anonymous

int main()
try {
using namespace sw::universal;
std::string test_suite = "elreal block<FpType> accessors";
int nrOfFailedTestCases = 0;
bool reportTestCases = false;
ReportTestSuiteHeader(test_suite, reportTestCases);

nrOfFailedTestCases += verify_accessors<float>("block<float>");
nrOfFailedTestCases += verify_accessors<double>("block<double>");
nrOfFailedTestCases += verify_accessors<half>("block<half>");
nrOfFailedTestCases += verify_accessors<bfloat16>("block<bfloat16>");
nrOfFailedTestCases += verify_accessors<cfloat<24, 5, std::uint16_t, true, false, false>>("block<cfloat<24,5>>");
nrOfFailedTestCases += verify_accessors<cfloat<32, 8, std::uint32_t, true, false, false>>("block<cfloat<32,8>>");

ReportTestSuiteResults(test_suite, nrOfFailedTestCases);
return (nrOfFailedTestCases > 0 ? EXIT_FAILURE : EXIT_SUCCESS);
}
catch (const std::exception& err) {
std::cerr << "Caught unexpected exception: " << err.what() << std::endl;
return EXIT_FAILURE;
}
92 changes: 92 additions & 0 deletions elastic/elreal/block/construction.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// construction.cpp: trivial-layout + value construction tests for elreal block<FpType>.
//
// Phase 1 (#925) acceptance: block must be a trivial type so it can sit on
// hardware boundaries (FPGA, accelerator) without an out-of-band constructor.
//
// 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 <universal/utility/directives.hpp>
#include <iostream>
#include <type_traits>

#include <universal/number/cfloat/cfloat.hpp>
#include <universal/number/bfloat16/bfloat16.hpp>
#include <universal/number/elreal/elreal.hpp>
#include <universal/verification/test_suite.hpp>

namespace {

template <typename FpType>
int verify_construction(const std::string& tag) {
using namespace sw::universal;
using B = block<FpType>;

int nrFailures = 0;

static_assert(std::is_trivially_copyable_v<B>,
"block<FpType> must be trivially copyable");
static_assert(std::is_trivially_destructible_v<B>,
"block<FpType> must be trivially destructible");
// Default constructibility relaxed: block leaves members indeterminate by
// design (matches the project's other trivial number types). We require
// copy-constructibility instead.
static_assert(std::is_copy_constructible_v<B>);
static_assert(std::is_copy_assignable_v<B>);

// value construction
B one{ FpType{1}, 0 };
if (one.v != FpType{1}) { std::cout << tag << ": one.v != 1\n"; ++nrFailures; }
if (one.exp_offset != 0) { std::cout << tag << ": one.exp_offset != 0\n"; ++nrFailures; }

// value + offset construction
B big{ FpType{1.5}, 3 };
if (big.v != FpType{1.5}) { std::cout << tag << ": big.v != 1.5\n"; ++nrFailures; }
if (big.exp_offset != 3) { std::cout << tag << ": big.exp_offset != 3\n"; ++nrFailures; }

// negative offset
B small{ FpType{1.5}, -5 };
if (small.exp_offset != -5) { std::cout << tag << ": small.exp_offset != -5\n"; ++nrFailures; }

// copy
B copy = big;
if (copy.v != big.v || copy.exp_offset != big.exp_offset) {
std::cout << tag << ": copy mismatch\n"; ++nrFailures;
}

// compile-time constants exposed on the type
static_assert(B::k > 0, "block::k must be positive");
static_assert(B::E > 0, "block::E must be positive");
static_assert(B::exp_step > 0, "block::exp_step must be positive");

std::cout << tag << " k=" << B::k << " E=" << B::E
<< " exp_step=" << B::exp_step << "\n";

return nrFailures;
}

} // anonymous

int main()
try {
using namespace sw::universal;
std::string test_suite = "elreal block<FpType> construction";
int nrOfFailedTestCases = 0;
bool reportTestCases = false;
ReportTestSuiteHeader(test_suite, reportTestCases);

nrOfFailedTestCases += verify_construction<float>("block<float>");
nrOfFailedTestCases += verify_construction<double>("block<double>");
nrOfFailedTestCases += verify_construction<half>("block<half>");
nrOfFailedTestCases += verify_construction<bfloat16>("block<bfloat16>");
nrOfFailedTestCases += verify_construction<cfloat<24, 5, std::uint16_t, true, false, false>>("block<cfloat<24,5>>");
nrOfFailedTestCases += verify_construction<cfloat<32, 8, std::uint32_t, true, false, false>>("block<cfloat<32,8>>");

ReportTestSuiteResults(test_suite, nrOfFailedTestCases);
return (nrOfFailedTestCases > 0 ? EXIT_FAILURE : EXIT_SUCCESS);
}
catch (const std::exception& err) {
std::cerr << "Caught unexpected exception: " << err.what() << std::endl;
return EXIT_FAILURE;
}
98 changes: 98 additions & 0 deletions elastic/elreal/block/exp_offset.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// exp_offset.cpp: int32_t exp_offset boundary tests for elreal block<FpType>.
//
// The block's exp_offset multiplies in units of 2^E. Its purpose is to escape
// the host FpType's hardware exponent range, which matters for transcendentals
// at high depth in later phases.
//
// 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 <universal/utility/directives.hpp>
#include <cstdint>
#include <iostream>
#include <limits>

#include <universal/number/cfloat/cfloat.hpp>
#include <universal/number/bfloat16/bfloat16.hpp>
#include <universal/number/elreal/elreal.hpp>
#include <universal/verification/test_suite.hpp>

namespace {

template <typename FpType>
int verify_exp_offset(const std::string& tag) {
using namespace sw::universal;
using B = block<FpType>;
int nrFailures = 0;

// exp_offset = 0: exponent() == scale_of_v()
B at_one{ FpType{1.5}, 0 };
if (at_one.exponent() != at_one.scale_of_v()) {
std::cout << tag << " offset=0 mismatch\n"; ++nrFailures;
}

// Positive offset levering: each +1 step adds exp_step.
B step_up{ FpType{1.5}, 1 };
std::int64_t expected = at_one.scale_of_v() + B::exp_step;
if (step_up.exponent() != expected) {
std::cout << tag << " offset=+1 wrong: got " << step_up.exponent()
<< " expected " << expected << '\n';
++nrFailures;
}

// Negative offset levering: each -1 step subtracts exp_step.
B step_down{ FpType{1.5}, -1 };
std::int64_t expected_dn = at_one.scale_of_v() - B::exp_step;
if (step_down.exponent() != expected_dn) {
std::cout << tag << " offset=-1 wrong: got " << step_down.exponent()
<< " expected " << expected_dn << '\n';
++nrFailures;
}

// Large positive offset: enough to push the block's exponent far beyond
// FpType's hardware exponent range -- this is the whole point.
B very_high{ FpType{1.5}, 100 };
std::int64_t expected_high = at_one.scale_of_v() + std::int64_t(100) * B::exp_step;
if (very_high.exponent() != expected_high) {
std::cout << tag << " offset=+100 wrong: got " << very_high.exponent()
<< " expected " << expected_high << '\n';
++nrFailures;
}

// Extreme int32 boundary: exp_offset = INT32_MAX. The exponent() computation
// must not overflow int (it returns int64).
B extreme{ FpType{1.5}, std::numeric_limits<std::int32_t>::max() };
std::int64_t expected_ext = at_one.scale_of_v()
+ static_cast<std::int64_t>(std::numeric_limits<std::int32_t>::max()) * B::exp_step;
if (extreme.exponent() != expected_ext) {
std::cout << tag << " offset=INT32_MAX wrong\n"; ++nrFailures;
}

return nrFailures;
}

} // anonymous

int main()
try {
using namespace sw::universal;
std::string test_suite = "elreal block<FpType> exp_offset boundary";
int nrOfFailedTestCases = 0;
bool reportTestCases = false;
ReportTestSuiteHeader(test_suite, reportTestCases);

nrOfFailedTestCases += verify_exp_offset<float>("block<float>");
nrOfFailedTestCases += verify_exp_offset<double>("block<double>");
nrOfFailedTestCases += verify_exp_offset<half>("block<half>");
nrOfFailedTestCases += verify_exp_offset<bfloat16>("block<bfloat16>");
nrOfFailedTestCases += verify_exp_offset<cfloat<24, 5, std::uint16_t, true, false, false>>("block<cfloat<24,5>>");
nrOfFailedTestCases += verify_exp_offset<cfloat<32, 8, std::uint32_t, true, false, false>>("block<cfloat<32,8>>");

ReportTestSuiteResults(test_suite, nrOfFailedTestCases);
return (nrOfFailedTestCases > 0 ? EXIT_FAILURE : EXIT_SUCCESS);
}
catch (const std::exception& err) {
std::cerr << "Caught unexpected exception: " << err.what() << std::endl;
return EXIT_FAILURE;
}
Loading
Loading