From b9f700341218f33cb29c632339ede00d161a3e30 Mon Sep 17 00:00:00 2001 From: Theodore Omtzigt Date: Sun, 22 Oct 2023 10:31:23 -0400 Subject: [PATCH] V3.73 (#374) * updating SEMVER to v3.73.1 * updating CI to build branch v3.73 * code hygiene for benchmarking * adding dynamic range benchmarking * code hygiene * WIP: adding a home for parameterized fma function * WIP: bfloat16 refinement --- CMakeLists.txt | 8 + benchmark/accuracy/blas/dot.cpp | 22 +- benchmark/accuracy/blas/gemm.cpp | 11 +- benchmark/accuracy/blas/matvec.cpp | 13 +- benchmark/energy/blas/dot.cpp | 22 +- benchmark/energy/blas/gemm.cpp | 11 +- benchmark/energy/blas/matvec.cpp | 13 +- benchmark/error/blas/dot.cpp | 11 +- benchmark/error/sampling/sampling.cpp | 2 +- benchmark/performance/blas/dot.cpp | 22 +- benchmark/performance/blas/gemm.cpp | 11 +- benchmark/performance/blas/matvec.cpp | 11 +- benchmark/range/arithmetic/CMakeLists.txt | 3 + benchmark/range/arithmetic/floating-point.cpp | 175 +++++ benchmark/range/blas/CMakeLists.txt | 3 + benchmark/range/blas/dot.cpp | 64 ++ benchmark/range/blas/gemm.cpp | 77 +++ benchmark/range/blas/matvec.cpp | 72 +++ benchmark/reproducibility/blas/dot.cpp | 22 +- benchmark/reproducibility/blas/gemm.cpp | 11 +- benchmark/reproducibility/blas/matvec.cpp | 13 +- docker/build_release_container.sh | 4 - docker/build_test_container.sh | 4 - include/universal/blas/blas_l1.hpp | 4 +- include/universal/blas/generators/frank.hpp | 10 +- include/universal/blas/matrix.hpp | 4 +- include/universal/blas/operators.hpp | 2 +- .../universal/internal/bitblock/bitblock.hpp | 2 +- include/universal/internal/value/value.hpp | 2 +- include/universal/number/bfloat/bfloat.hpp | 3 + .../universal/number/bfloat/bfloat16_impl.hpp | 129 +++- .../universal/number/bfloat/bfloat8_fwd.hpp | 14 + .../universal/number/bfloat/bfloat8_impl.hpp | 599 ++++++++++++++++++ .../universal/number/bfloat/manipulators.hpp | 116 +++- include/universal/number/cfloat/cfloat.hpp | 2 +- .../number/faithful/faithful_impl.hpp | 2 +- include/universal/number/posit/exponent.hpp | 2 +- include/universal/number/posit/fraction.hpp | 2 +- include/universal/number/posit/posit.hpp | 4 +- include/universal/number/posit/posit_impl.hpp | 4 +- .../{analysis => numerics}/README.md | 0 .../universal/{analysis => numerics}/eft.hpp | 6 +- .../{analysis => numerics}/twodiv.hpp | 0 .../{analysis => numerics}/twoprod.hpp | 0 .../{analysis => numerics}/twosum.hpp | 1 - include/universal/traits/bfloat16_traits.hpp | 31 + include/universal/traits/bfloat8_traits.hpp | 31 + numeric/faithful/accurate_sum_and_dot.cpp | 9 +- numeric/faithful/arithmetic.cpp | 43 +- static/bfloat/api/api.cpp | 138 ++-- static/bfloat/api/attributes.cpp | 6 +- 51 files changed, 1543 insertions(+), 228 deletions(-) create mode 100644 benchmark/range/arithmetic/CMakeLists.txt create mode 100644 benchmark/range/arithmetic/floating-point.cpp create mode 100644 benchmark/range/blas/CMakeLists.txt create mode 100644 benchmark/range/blas/dot.cpp create mode 100644 benchmark/range/blas/gemm.cpp create mode 100644 benchmark/range/blas/matvec.cpp create mode 100644 include/universal/number/bfloat/bfloat8_fwd.hpp create mode 100644 include/universal/number/bfloat/bfloat8_impl.hpp rename include/universal/{analysis => numerics}/README.md (100%) rename include/universal/{analysis => numerics}/eft.hpp (69%) rename include/universal/{analysis => numerics}/twodiv.hpp (100%) rename include/universal/{analysis => numerics}/twoprod.hpp (100%) rename include/universal/{analysis => numerics}/twosum.hpp (99%) create mode 100644 include/universal/traits/bfloat16_traits.hpp create mode 100644 include/universal/traits/bfloat8_traits.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index f7fa4bbb8..4e22ad058 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -145,6 +145,7 @@ option(BUILD_LINEAR_ALGEBRA_DATA "Set to ON to build the data prep math # benchmarking option(BUILD_BENCHMARK_ERROR "Set to ON to build error benchmarks" OFF) option(BUILD_BENCHMARK_ACCURACY "Set to ON to build accuracy benchmarks" OFF) +option(BUILD_BENCHMARK_RANGE "Set to ON to build dynamic range benchmarks" OFF) option(BUILD_BENCHMARK_REPRODUCIBILITY "Set to ON to build reproducibility benchmarks" OFF) option(BUILD_BENCHMARK_PERFORMANCE "Set to ON to build performance benchmarks" OFF) option(BUILD_BENCHMARK_ENERGY "Set to ON to build energy efficiency benchmarks" OFF) @@ -658,6 +659,7 @@ endif(BUILD_NUMERICS) if(BUILD_BENCHMARKS) set(BUILD_BENCHMARK_ERROR ON) set(BUILD_BENCHMARK_ACCURACY ON) + set(BUILD_BENCHMARK_RANGE ON) set(BUILD_BENCHMARK_REPRODUCIBILITY ON) set(BUILD_BENCHMARK_ENERGY ON) set(BUILD_BENCHMARK_PERFORMANCE ON) @@ -833,6 +835,12 @@ add_subdirectory("benchmark/accuracy/blas") add_subdirectory("benchmark/accuracy/quantization") endif(BUILD_BENCHMARK_ACCURACY) +# range benchmarks +if(BUILD_BENCHMARK_RANGE) +add_subdirectory("benchmark/range/blas") +add_subdirectory("benchmark/range/arithmetic") +endif(BUILD_BENCHMARK_RANGE) + # reproducibility benchmarks if(BUILD_BENCHMARK_REPRODUCIBILITY) add_subdirectory("benchmark/reproducibility/blas") diff --git a/benchmark/accuracy/blas/dot.cpp b/benchmark/accuracy/blas/dot.cpp index f94d9211b..7ff6bdddf 100644 --- a/benchmark/accuracy/blas/dot.cpp +++ b/benchmark/accuracy/blas/dot.cpp @@ -3,17 +3,7 @@ // Copyright (C) 2017-2023 Stillwater Supercomputing, Inc. // // This file is part of the universal numbers project, which is released under an MIT Open Source license. -#ifdef _MSC_VER -#pragma warning(disable : 4514) // warning C4514: 'std::complex::complex': unreferenced inline function has been removed -#pragma warning(disable : 4571) // warning C4571: Informational: catch(...) semantics changed since Visual C++ 7.1; structured exceptions (SEH) are no longer caught -#pragma warning(disable : 4625) // warning C4625: 'std::moneypunct': copy constructor was implicitly defined as deleted -#pragma warning(disable : 4626) // warning C4626: 'std::codecvt_base': assignment operator was implicitly defined as deleted -#pragma warning(disable : 4710) // warning C4710: 'int swprintf_s(wchar_t *const ,const size_t,const wchar_t *const ,...)': function not inlined -#pragma warning(disable : 4774) // warning C4774: 'sprintf_s' : format string expected in argument 3 is not a string literal -#pragma warning(disable : 4820) // warning C4820: 'std::_Mpunct<_Elem>': '4' bytes padding added after data member 'std::_Mpunct<_Elem>::_Kseparator' -#pragma warning(disable : 5026) // warning C5026 : 'std::_Generic_error_category' : move constructor was implicitly defined as deleted -#pragma warning(disable : 5027) // warning C5027 : 'std::_Generic_error_category' : move assignment operator was implicitly defined as deleted -#endif +#include // enable the following define to show the intermediate steps in the fused-dot product // #define ALGORITHM_VERBOSE_OUTPUT @@ -30,7 +20,7 @@ #include #include -int main(int argc, char** argv) +int main() try { using namespace sw::universal; @@ -63,16 +53,16 @@ catch (char const* msg) { std::cerr << msg << std::endl; return EXIT_FAILURE; } -catch (const sw::universal::posit_arithmetic_exception& err) { - std::cerr << "Uncaught posit arithmetic exception: " << err.what() << std::endl; +catch (const sw::universal::universal_arithmetic_exception& err) { + std::cerr << "Uncaught universal arithmetic exception: " << err.what() << std::endl; return EXIT_FAILURE; } catch (const sw::universal::quire_exception& err) { std::cerr << "Uncaught quire exception: " << err.what() << std::endl; return EXIT_FAILURE; } -catch (const sw::universal::posit_internal_exception& err) { - std::cerr << "Uncaught posit internal exception: " << err.what() << std::endl; +catch (const sw::universal::universal_internal_exception& err) { + std::cerr << "Uncaught universal internal exception: " << err.what() << std::endl; return EXIT_FAILURE; } catch (const std::runtime_error& err) { diff --git a/benchmark/accuracy/blas/gemm.cpp b/benchmark/accuracy/blas/gemm.cpp index e39bffde5..2cdf263d1 100644 --- a/benchmark/accuracy/blas/gemm.cpp +++ b/benchmark/accuracy/blas/gemm.cpp @@ -3,6 +3,7 @@ // Copyright (C) 2017-2023 Stillwater Supercomputing, Inc. // // This file is part of the universal numbers project, which is released under an MIT Open Source license. +#include // enable the following define to show the intermediate steps in the fused-dot product // #define ALGORITHM_VERBOSE_OUTPUT @@ -37,7 +38,7 @@ sw::universal::occurrence sw::universal::edecimal::ops; #endif -int main(int argc, char** argv) +int main() try { using namespace sw::universal::blas; @@ -60,16 +61,16 @@ catch (char const* msg) { std::cerr << msg << std::endl; return EXIT_FAILURE; } -catch (const sw::universal::posit_arithmetic_exception& err) { - std::cerr << "Uncaught posit arithmetic exception: " << err.what() << std::endl; +catch (const sw::universal::universal_arithmetic_exception& err) { + std::cerr << "Uncaught universal arithmetic exception: " << err.what() << std::endl; return EXIT_FAILURE; } catch (const sw::universal::quire_exception& err) { std::cerr << "Uncaught quire exception: " << err.what() << std::endl; return EXIT_FAILURE; } -catch (const sw::universal::posit_internal_exception& err) { - std::cerr << "Uncaught posit internal exception: " << err.what() << std::endl; +catch (const sw::universal::universal_internal_exception& err) { + std::cerr << "Uncaught universal internal exception: " << err.what() << std::endl; return EXIT_FAILURE; } catch (const std::runtime_error& err) { diff --git a/benchmark/accuracy/blas/matvec.cpp b/benchmark/accuracy/blas/matvec.cpp index 0b9b444e5..3575c4d8d 100644 --- a/benchmark/accuracy/blas/matvec.cpp +++ b/benchmark/accuracy/blas/matvec.cpp @@ -1,8 +1,9 @@ // matvec.cpp: accuracy/precision measurment of mixed-precision matrix-vector product // -// Copyright (C) 2017-2021 Stillwater Supercomputing, Inc. +// Copyright (C) 2017-2023 Stillwater Supercomputing, Inc. // // This file is part of the universal numbers project, which is released under an MIT Open Source license. +#include // enable the following define to show the intermediate steps in the fused-dot product // #define ALGORITHM_VERBOSE_OUTPUT @@ -43,7 +44,7 @@ void catastrophicCancellationTest() { } } -int main(int argc, char** argv) +int main() try { catastrophicCancellationTest(); catastrophicCancellationTest(); @@ -55,16 +56,16 @@ catch (char const* msg) { std::cerr << msg << std::endl; return EXIT_FAILURE; } -catch (const sw::universal::posit_arithmetic_exception& err) { - std::cerr << "Uncaught posit arithmetic exception: " << err.what() << std::endl; +catch (const sw::universal::universal_arithmetic_exception& err) { + std::cerr << "Uncaught universal arithmetic exception: " << err.what() << std::endl; return EXIT_FAILURE; } catch (const sw::universal::quire_exception& err) { std::cerr << "Uncaught quire exception: " << err.what() << std::endl; return EXIT_FAILURE; } -catch (const sw::universal::posit_internal_exception& err) { - std::cerr << "Uncaught posit internal exception: " << err.what() << std::endl; +catch (const sw::universal::universal_internal_exception& err) { + std::cerr << "Uncaught universal internal exception: " << err.what() << std::endl; return EXIT_FAILURE; } catch (const std::runtime_error& err) { diff --git a/benchmark/energy/blas/dot.cpp b/benchmark/energy/blas/dot.cpp index b0adb8419..bf99dc26d 100644 --- a/benchmark/energy/blas/dot.cpp +++ b/benchmark/energy/blas/dot.cpp @@ -3,17 +3,7 @@ // Copyright (C) 2017-2023 Stillwater Supercomputing, Inc. // // This file is part of the universal numbers project, which is released under an MIT Open Source license. -#ifdef _MSC_VER -#pragma warning(disable : 4514) // warning C4514: 'std::complex::complex': unreferenced inline function has been removed -#pragma warning(disable : 4571) // warning C4571: Informational: catch(...) semantics changed since Visual C++ 7.1; structured exceptions (SEH) are no longer caught -#pragma warning(disable : 4625) // warning C4625: 'std::moneypunct': copy constructor was implicitly defined as deleted -#pragma warning(disable : 4626) // warning C4626: 'std::codecvt_base': assignment operator was implicitly defined as deleted -#pragma warning(disable : 4710) // warning C4710: 'int swprintf_s(wchar_t *const ,const size_t,const wchar_t *const ,...)': function not inlined -#pragma warning(disable : 4774) // warning C4774: 'sprintf_s' : format string expected in argument 3 is not a string literal -#pragma warning(disable : 4820) // warning C4820: 'std::_Mpunct<_Elem>': '4' bytes padding added after data member 'std::_Mpunct<_Elem>::_Kseparator' -#pragma warning(disable : 5026) // warning C5026 : 'std::_Generic_error_category' : move constructor was implicitly defined as deleted -#pragma warning(disable : 5027) // warning C5027 : 'std::_Generic_error_category' : move assignment operator was implicitly defined as deleted -#endif +#include // enable the following define to show the intermediate steps in the fused-dot product // #define ALGORITHM_VERBOSE_OUTPUT @@ -30,7 +20,7 @@ #include #include -int main(int argc, char** argv) +int main() try { using namespace sw::universal; @@ -63,16 +53,16 @@ catch (char const* msg) { std::cerr << msg << std::endl; return EXIT_FAILURE; } -catch (const sw::universal::posit_arithmetic_exception& err) { - std::cerr << "Uncaught posit arithmetic exception: " << err.what() << std::endl; +catch (const sw::universal::universal_arithmetic_exception& err) { + std::cerr << "Uncaught universal arithmetic exception: " << err.what() << std::endl; return EXIT_FAILURE; } catch (const sw::universal::quire_exception& err) { std::cerr << "Uncaught quire exception: " << err.what() << std::endl; return EXIT_FAILURE; } -catch (const sw::universal::posit_internal_exception& err) { - std::cerr << "Uncaught posit internal exception: " << err.what() << std::endl; +catch (const sw::universal::universal_internal_exception& err) { + std::cerr << "Uncaught universal internal exception: " << err.what() << std::endl; return EXIT_FAILURE; } catch (const std::runtime_error& err) { diff --git a/benchmark/energy/blas/gemm.cpp b/benchmark/energy/blas/gemm.cpp index 1ff858a44..278a5914b 100644 --- a/benchmark/energy/blas/gemm.cpp +++ b/benchmark/energy/blas/gemm.cpp @@ -3,6 +3,7 @@ // Copyright (C) 2017-2023 Stillwater Supercomputing, Inc. // // This file is part of the universal numbers project, which is released under an MIT Open Source license. +#include // enable the following define to show the intermediate steps in the fused-dot product // #define ALGORITHM_VERBOSE_OUTPUT @@ -37,7 +38,7 @@ sw::universal::occurrence sw::universal::edecimal::ops; #endif -int main(int argc, char** argv) +int main() try { using namespace sw::universal::blas; @@ -60,16 +61,16 @@ catch (char const* msg) { std::cerr << msg << std::endl; return EXIT_FAILURE; } -catch (const sw::universal::posit_arithmetic_exception& err) { - std::cerr << "Uncaught posit arithmetic exception: " << err.what() << std::endl; +catch (const sw::universal::universal_arithmetic_exception& err) { + std::cerr << "Uncaught universal arithmetic exception: " << err.what() << std::endl; return EXIT_FAILURE; } catch (const sw::universal::quire_exception& err) { std::cerr << "Uncaught quire exception: " << err.what() << std::endl; return EXIT_FAILURE; } -catch (const sw::universal::posit_internal_exception& err) { - std::cerr << "Uncaught posit internal exception: " << err.what() << std::endl; +catch (const sw::universal::universal_internal_exception& err) { + std::cerr << "Uncaught universal internal exception: " << err.what() << std::endl; return EXIT_FAILURE; } catch (const std::runtime_error& err) { diff --git a/benchmark/energy/blas/matvec.cpp b/benchmark/energy/blas/matvec.cpp index d21923196..8f73745ce 100644 --- a/benchmark/energy/blas/matvec.cpp +++ b/benchmark/energy/blas/matvec.cpp @@ -1,8 +1,9 @@ // matvec.cpp: energy measurement of a mixed-precision matrix-vector product // -// Copyright (C) 2017-2021 Stillwater Supercomputing, Inc. +// Copyright (C) 2017-2023 Stillwater Supercomputing, Inc. // // This file is part of the universal numbers project, which is released under an MIT Open Source license. +#include // enable the following define to show the intermediate steps in the fused-dot product // #define ALGORITHM_VERBOSE_OUTPUT @@ -43,7 +44,7 @@ void catastrophicCancellationTest() { } } -int main(int argc, char** argv) +int main() try { catastrophicCancellationTest(); catastrophicCancellationTest(); @@ -55,16 +56,16 @@ catch (char const* msg) { std::cerr << msg << std::endl; return EXIT_FAILURE; } -catch (const sw::universal::posit_arithmetic_exception& err) { - std::cerr << "Uncaught posit arithmetic exception: " << err.what() << std::endl; +catch (const sw::universal::universal_arithmetic_exception& err) { + std::cerr << "Uncaught universal arithmetic exception: " << err.what() << std::endl; return EXIT_FAILURE; } catch (const sw::universal::quire_exception& err) { std::cerr << "Uncaught quire exception: " << err.what() << std::endl; return EXIT_FAILURE; } -catch (const sw::universal::posit_internal_exception& err) { - std::cerr << "Uncaught posit internal exception: " << err.what() << std::endl; +catch (const sw::universal::universal_internal_exception& err) { + std::cerr << "Uncaught universal internal exception: " << err.what() << std::endl; return EXIT_FAILURE; } catch (const std::runtime_error& err) { diff --git a/benchmark/error/blas/dot.cpp b/benchmark/error/blas/dot.cpp index 04c11bc7f..9ec01485e 100644 --- a/benchmark/error/blas/dot.cpp +++ b/benchmark/error/blas/dot.cpp @@ -32,9 +32,10 @@ void TraceProducts(const sw::universal::blas::vector& x, const sw::unive } std::cout << "input range = [ " << minInput << ", " << maxInput << "]\n"; std::cout << "product range = [ " << minProduct << ", " << maxProduct << "]\n"; - std::cout << sw::universal::symmetry_range() << '\n'; - // std::cout << sw::universal::dynamic_range() << '\n'; // not that interesting in this context - std::cout << sw::universal::minmax_range() << '\n'; + std::string infoStr = sw::universal::symmetry_range(); + std::cout << infoStr << '\n'; + infoStr = sw::universal::minmax_range(); + std::cout << infoStr << '\n'; } template @@ -43,8 +44,8 @@ void DotProductError(const sw::universal::blas::vector& x, double minx, using namespace sw::universal; std::cout << "\nScalar type : " << type_tag(Scalar()) << '\n'; - auto minpos = double(std::numeric_limits::min()); - auto maxpos = double(std::numeric_limits::max()); + auto minpos = static_cast(std::numeric_limits::min()); + auto maxpos = static_cast(std::numeric_limits::max()); auto maxxy = std::max(maxx, maxy); auto focus{ 1.0 }, expand{ 1.0 }; if (maxxy*maxxy > maxpos) { // need to scale the vectors diff --git a/benchmark/error/sampling/sampling.cpp b/benchmark/error/sampling/sampling.cpp index fe9bfb062..b6f0e109c 100644 --- a/benchmark/error/sampling/sampling.cpp +++ b/benchmark/error/sampling/sampling.cpp @@ -1,6 +1,6 @@ // sampling.cpp: error measurement of the approximation of a number system sampling real values // -// Copyright (C) 2022-2022 Stillwater Supercomputing, Inc. +// Copyright (C) 2022-2023 Stillwater Supercomputing, Inc. // // This file is part of the universal numbers project, which is released under an MIT Open Source license. #include diff --git a/benchmark/performance/blas/dot.cpp b/benchmark/performance/blas/dot.cpp index 192fbcc14..53da2cab9 100644 --- a/benchmark/performance/blas/dot.cpp +++ b/benchmark/performance/blas/dot.cpp @@ -3,17 +3,7 @@ // Copyright (C) 2017-2023 Stillwater Supercomputing, Inc. // // This file is part of the universal numbers project, which is released under an MIT Open Source license. -#ifdef _MSC_VER -#pragma warning(disable : 4514) // warning C4514: 'std::complex::complex': unreferenced inline function has been removed -#pragma warning(disable : 4571) // warning C4571: Informational: catch(...) semantics changed since Visual C++ 7.1; structured exceptions (SEH) are no longer caught -#pragma warning(disable : 4625) // warning C4625: 'std::moneypunct': copy constructor was implicitly defined as deleted -#pragma warning(disable : 4626) // warning C4626: 'std::codecvt_base': assignment operator was implicitly defined as deleted -#pragma warning(disable : 4710) // warning C4710: 'int swprintf_s(wchar_t *const ,const size_t,const wchar_t *const ,...)': function not inlined -#pragma warning(disable : 4774) // warning C4774: 'sprintf_s' : format string expected in argument 3 is not a string literal -#pragma warning(disable : 4820) // warning C4820: 'std::_Mpunct<_Elem>': '4' bytes padding added after data member 'std::_Mpunct<_Elem>::_Kseparator' -#pragma warning(disable : 5026) // warning C5026 : 'std::_Generic_error_category' : move constructor was implicitly defined as deleted -#pragma warning(disable : 5027) // warning C5027 : 'std::_Generic_error_category' : move assignment operator was implicitly defined as deleted -#endif +#include // enable the following define to show the intermediate steps in the fused-dot product // #define ALGORITHM_VERBOSE_OUTPUT @@ -30,7 +20,7 @@ #include #include -int main(int argc, char** argv) +int main() try { using namespace sw::universal; @@ -63,16 +53,16 @@ catch (char const* msg) { std::cerr << "Caught exception: " << msg << std::endl; return EXIT_FAILURE; } -catch (const sw::universal::posit_arithmetic_exception& err) { - std::cerr << "Uncaught posit arithmetic exception: " << err.what() << std::endl; +catch (const sw::universal::universal_arithmetic_exception& err) { + std::cerr << "Uncaught universal arithmetic exception: " << err.what() << std::endl; return EXIT_FAILURE; } catch (const sw::universal::quire_exception& err) { std::cerr << "Uncaught quire exception: " << err.what() << std::endl; return EXIT_FAILURE; } -catch (const sw::universal::posit_internal_exception& err) { - std::cerr << "Uncaught posit internal exception: " << err.what() << std::endl; +catch (const sw::universal::universal_internal_exception& err) { + std::cerr << "Uncaught universal internal exception: " << err.what() << std::endl; return EXIT_FAILURE; } catch (const std::runtime_error& err) { diff --git a/benchmark/performance/blas/gemm.cpp b/benchmark/performance/blas/gemm.cpp index 5991ba840..588c72e35 100644 --- a/benchmark/performance/blas/gemm.cpp +++ b/benchmark/performance/blas/gemm.cpp @@ -3,6 +3,7 @@ // Copyright (C) 2017-2023 Stillwater Supercomputing, Inc. // // This file is part of the universal numbers project, which is released under an MIT Open Source license. +#include // enable the following define to show the intermediate steps in the fused-dot product // #define ALGORITHM_VERBOSE_OUTPUT @@ -37,7 +38,7 @@ sw::universal::occurrence sw::universal::edecimal::ops; #endif -int main(int argc, char** argv) +int main() try { using namespace sw::universal::blas; @@ -60,16 +61,16 @@ catch (char const* msg) { std::cerr << "Caught exception: " << msg << std::endl; return EXIT_FAILURE; } -catch (const sw::universal::posit_arithmetic_exception& err) { - std::cerr << "Uncaught posit arithmetic exception: " << err.what() << std::endl; +catch (const sw::universal::universal_arithmetic_exception& err) { + std::cerr << "Uncaught universal arithmetic exception: " << err.what() << std::endl; return EXIT_FAILURE; } catch (const sw::universal::quire_exception& err) { std::cerr << "Uncaught quire exception: " << err.what() << std::endl; return EXIT_FAILURE; } -catch (const sw::universal::posit_internal_exception& err) { - std::cerr << "Uncaught posit internal exception: " << err.what() << std::endl; +catch (const sw::universal::universal_internal_exception& err) { + std::cerr << "Uncaught universal internal exception: " << err.what() << std::endl; return EXIT_FAILURE; } catch (const std::runtime_error& err) { diff --git a/benchmark/performance/blas/matvec.cpp b/benchmark/performance/blas/matvec.cpp index 3d215e2e2..647dbe73b 100644 --- a/benchmark/performance/blas/matvec.cpp +++ b/benchmark/performance/blas/matvec.cpp @@ -3,6 +3,7 @@ // Copyright (C) 2017-2021 Stillwater Supercomputing, Inc. // // This file is part of the universal numbers project, which is released under an MIT Open Source license. +#include // enable the following define to show the intermediate steps in the fused-dot product // #define ALGORITHM_VERBOSE_OUTPUT @@ -43,7 +44,7 @@ void catastrophicCancellationTest() { } } -int main(int argc, char** argv) +int main() try { catastrophicCancellationTest(); catastrophicCancellationTest(); @@ -55,16 +56,16 @@ catch (char const* msg) { std::cerr << "Caught exception: " << msg << std::endl; return EXIT_FAILURE; } -catch (const sw::universal::posit_arithmetic_exception& err) { - std::cerr << "Uncaught posit arithmetic exception: " << err.what() << std::endl; +catch (const sw::universal::universal_arithmetic_exception& err) { + std::cerr << "Uncaught universal arithmetic exception: " << err.what() << std::endl; return EXIT_FAILURE; } catch (const sw::universal::quire_exception& err) { std::cerr << "Uncaught quire exception: " << err.what() << std::endl; return EXIT_FAILURE; } -catch (const sw::universal::posit_internal_exception& err) { - std::cerr << "Uncaught posit internal exception: " << err.what() << std::endl; +catch (const sw::universal::universal_internal_exception& err) { + std::cerr << "Uncaught universal internal exception: " << err.what() << std::endl; return EXIT_FAILURE; } catch (const std::runtime_error& err) { diff --git a/benchmark/range/arithmetic/CMakeLists.txt b/benchmark/range/arithmetic/CMakeLists.txt new file mode 100644 index 000000000..4e0f830ef --- /dev/null +++ b/benchmark/range/arithmetic/CMakeLists.txt @@ -0,0 +1,3 @@ +file (GLOB SOURCES "./*.cpp") + +compile_all("true" "range" "Benchmarks/Dynamic Range/Arithmetic" "${SOURCES}") diff --git a/benchmark/range/arithmetic/floating-point.cpp b/benchmark/range/arithmetic/floating-point.cpp new file mode 100644 index 000000000..38d17c589 --- /dev/null +++ b/benchmark/range/arithmetic/floating-point.cpp @@ -0,0 +1,175 @@ +// floating-point.cpp: dynamic range comparisons among floating-point types +// +// Copyright (C) 2017-2023 Stillwater Supercomputing, Inc. +// +// This file is part of the universal numbers project, which is released under an MIT Open Source license. +#include + +#include +#include +#include +#include + +#include + +int main() +try { + using namespace sw::universal; + + std::streamsize prec = std::cout.precision(); + std::cout << std::setprecision(17); + + { + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + } + { + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + } + { + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + } + { + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + } + { + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + } + { + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + } + { + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + } + + + { + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + } + { + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + } + { + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + } + { + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + } + { + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + } + + { + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + } + { + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + } + { + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + } + { + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + } + { + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + } + { + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + std::cout << symmetry_range>() << '\n'; + } + std::cout << std::setprecision(prec); + + return EXIT_SUCCESS; +} +catch (char const* msg) { + std::cerr << msg << std::endl; + return EXIT_FAILURE; +} +catch (const sw::universal::universal_arithmetic_exception& err) { + std::cerr << "Uncaught universal arithmetic exception: " << err.what() << std::endl; + return EXIT_FAILURE; +} +catch (const sw::universal::universal_internal_exception& err) { + std::cerr << "Uncaught universal internal exception: " << err.what() << std::endl; + return EXIT_FAILURE; +} +catch (const std::runtime_error& err) { + std::cerr << "Uncaught runtime exception: " << err.what() << std::endl; + return EXIT_FAILURE; +} +catch (...) { + std::cerr << "Caught unknown exception" << std::endl; + return EXIT_FAILURE; +} diff --git a/benchmark/range/blas/CMakeLists.txt b/benchmark/range/blas/CMakeLists.txt new file mode 100644 index 000000000..877bdd352 --- /dev/null +++ b/benchmark/range/blas/CMakeLists.txt @@ -0,0 +1,3 @@ +file (GLOB SOURCES "./*.cpp") + +compile_all("true" "range" "Benchmarks/Dynamic Range/BLAS" "${SOURCES}") diff --git a/benchmark/range/blas/dot.cpp b/benchmark/range/blas/dot.cpp new file mode 100644 index 000000000..527160709 --- /dev/null +++ b/benchmark/range/blas/dot.cpp @@ -0,0 +1,64 @@ +// dot.cpp: dynamic range measurement of mixed-precision dot product +// +// Copyright (C) 2017-2023 Stillwater Supercomputing, Inc. +// +// This file is part of the universal numbers project, which is released under an MIT Open Source license. +#include + +#include +#include +#include + +int main() +try { + using namespace sw::universal; + + std::streamsize prec = std::cout.precision(); + std::cout << std::setprecision(17); + + { + using Scalar = edecimal; + using Vector = sw::universal::blas::vector; +// Scalar a1 = 3.2e8, a2 = 1, a3 = -1, a4 = 8e7; // TODO: <--- bug conversion from double +// Scalar b1 = 4.0e7, b2 = 1, b3 = -1, b4 = -1.6e8; + Scalar a1 = 320'000'000, a2 = 1, a3 = -1, a4 = 80'000'000; + Scalar b1 = 40'000'000, b2 = 1, b3 = -1, b4 = -160'000'000; + Vector a = { a1, a2, a3, a4 }; + Vector b = { b1, b2, b3, b4 }; + + std::cout << "a: " << a << '\n'; + std::cout << "b: " << b << '\n'; + + std::cout << "\n\n"; + edecimal v = dot(a, b); + std::cout << v << (v == 2 ? " <----- PASS\n" : " <----- FAIL\n"); + } + + std::cout << std::setprecision(prec); + + return EXIT_SUCCESS; +} +catch (char const* msg) { + std::cerr << msg << std::endl; + return EXIT_FAILURE; +} +catch (const sw::universal::universal_arithmetic_exception& err) { + std::cerr << "Uncaught universal arithmetic exception: " << err.what() << std::endl; + return EXIT_FAILURE; +} +catch (const sw::universal::quire_exception& err) { + std::cerr << "Uncaught quire exception: " << err.what() << std::endl; + return EXIT_FAILURE; +} +catch (const sw::universal::universal_internal_exception& err) { + std::cerr << "Uncaught universal internal exception: " << err.what() << std::endl; + return EXIT_FAILURE; +} +catch (const std::runtime_error& err) { + std::cerr << "Uncaught runtime exception: " << err.what() << std::endl; + return EXIT_FAILURE; +} +catch (...) { + std::cerr << "Caught unknown exception" << std::endl; + return EXIT_FAILURE; +} diff --git a/benchmark/range/blas/gemm.cpp b/benchmark/range/blas/gemm.cpp new file mode 100644 index 000000000..eee640e27 --- /dev/null +++ b/benchmark/range/blas/gemm.cpp @@ -0,0 +1,77 @@ +// gemm.cpp: dynamic range measurement of mixed-precision general matrix-matrix product +// +// Copyright (C) 2017-2023 Stillwater Supercomputing, Inc. +// +// This file is part of the universal numbers project, which is released under an MIT Open Source license. +#include + +#include +// enable operation counts +#define EDECIMAL_OPERATIONS_COUNT 1 +#include + +#include +#include + +template +std::string conditional_fdp(const sw::universal::blas::vector< Scalar >& a, const sw::universal::blas::vector< Scalar >& b) { + return std::string("no FDP for non-posit value_type"); +} +template +std::string conditional_fdp(const sw::universal::blas::vector< sw::universal::posit >& a, const sw::universal::blas::vector< sw::universal::posit >& b) { + std::stringstream ss; + ss << sw::universal::fdp(a, b); + return ss.str(); +} + +#if EDECIMAL_OPERATIONS_COUNT + +// create the static storage for the occurrence measurements of the decimal number system +bool sw::universal::edecimal::enableAdd = true; +sw::universal::occurrence sw::universal::edecimal::ops; + +#endif + +int main() +try { + using namespace sw::universal::blas; + + using Scalar = sw::universal::edecimal; + using Matrix = matrix; + + constexpr size_t N = 5; + + Matrix A = eye(N); + Matrix B = frank(N); + sw::universal::edecimal proxy; + proxy.resetStats(); + Matrix C = A * B; + std::cout << C << '\n'; + proxy.printStats(std::cout); + + return EXIT_SUCCESS; +} +catch (char const* msg) { + std::cerr << msg << std::endl; + return EXIT_FAILURE; +} +catch (const sw::universal::universal_arithmetic_exception& err) { + std::cerr << "Uncaught universal arithmetic exception: " << err.what() << std::endl; + return EXIT_FAILURE; +} +catch (const sw::universal::quire_exception& err) { + std::cerr << "Uncaught quire exception: " << err.what() << std::endl; + return EXIT_FAILURE; +} +catch (const sw::universal::universal_internal_exception& err) { + std::cerr << "Uncaught universal internal exception: " << err.what() << std::endl; + return EXIT_FAILURE; +} +catch (const std::runtime_error& err) { + std::cerr << "Uncaught runtime exception: " << err.what() << std::endl; + return EXIT_FAILURE; +} +catch (...) { + std::cerr << "Caught unknown exception" << std::endl; + return EXIT_FAILURE; +} diff --git a/benchmark/range/blas/matvec.cpp b/benchmark/range/blas/matvec.cpp new file mode 100644 index 000000000..97dc2f776 --- /dev/null +++ b/benchmark/range/blas/matvec.cpp @@ -0,0 +1,72 @@ +// matvec.cpp: dynamic range measurment of mixed-precision matrix-vector product +// +// Copyright (C) 2017-2023 Stillwater Supercomputing, Inc. +// +// This file is part of the universal numbers project, which is released under an MIT Open Source license. +#include + +#include +#define BLAS_TRACE_ROUNDING_EVENTS 1 +#include + +template +void catastrophicCancellationTest() { + std::cout << "\nScalar type : " << typeid(Scalar).name() << '\n'; + using Matrix = sw::universal::blas::matrix; + using Vector = sw::universal::blas::vector; + + Scalar a1 = 3.2e8; + Scalar a2 = 1; + Scalar a3 = -1; + Scalar a4 = 8e7; + Matrix A = { + { a1, a2, a3, a4 }, + { a1, a2, a3, a4 } + }; + std::cout << std::setprecision(10); + std::cout << "matrix A: \n" << A << '\n'; + Vector x = { 4.0e7, 1, -1, -1.6e8 }; + std::cout << "vector x: \n" << x << '\n'; + Vector b(2); + b = A * x; + std::cout << "vector b: \n" << b << '\n'; + if (b[0] == 2 && b[1] == 2) { + std::cout << "PASS\n"; + } + else { + std::cout << "FAIL\n"; + } +} + +int main() +try { + catastrophicCancellationTest(); + catastrophicCancellationTest(); + catastrophicCancellationTest< sw::universal::posit<32,2> >(); + + return EXIT_SUCCESS; +} +catch (char const* msg) { + std::cerr << msg << std::endl; + return EXIT_FAILURE; +} +catch (const sw::universal::universal_arithmetic_exception& err) { + std::cerr << "Uncaught universal arithmetic exception: " << err.what() << std::endl; + return EXIT_FAILURE; +} +catch (const sw::universal::quire_exception& err) { + std::cerr << "Uncaught quire exception: " << err.what() << std::endl; + return EXIT_FAILURE; +} +catch (const sw::universal::universal_internal_exception& err) { + std::cerr << "Uncaught universal internal exception: " << err.what() << std::endl; + return EXIT_FAILURE; +} +catch (const std::runtime_error& err) { + std::cerr << "Uncaught runtime exception: " << err.what() << std::endl; + return EXIT_FAILURE; +} +catch (...) { + std::cerr << "Caught unknown exception" << std::endl; + return EXIT_FAILURE; +} diff --git a/benchmark/reproducibility/blas/dot.cpp b/benchmark/reproducibility/blas/dot.cpp index 192fbcc14..53da2cab9 100644 --- a/benchmark/reproducibility/blas/dot.cpp +++ b/benchmark/reproducibility/blas/dot.cpp @@ -3,17 +3,7 @@ // Copyright (C) 2017-2023 Stillwater Supercomputing, Inc. // // This file is part of the universal numbers project, which is released under an MIT Open Source license. -#ifdef _MSC_VER -#pragma warning(disable : 4514) // warning C4514: 'std::complex::complex': unreferenced inline function has been removed -#pragma warning(disable : 4571) // warning C4571: Informational: catch(...) semantics changed since Visual C++ 7.1; structured exceptions (SEH) are no longer caught -#pragma warning(disable : 4625) // warning C4625: 'std::moneypunct': copy constructor was implicitly defined as deleted -#pragma warning(disable : 4626) // warning C4626: 'std::codecvt_base': assignment operator was implicitly defined as deleted -#pragma warning(disable : 4710) // warning C4710: 'int swprintf_s(wchar_t *const ,const size_t,const wchar_t *const ,...)': function not inlined -#pragma warning(disable : 4774) // warning C4774: 'sprintf_s' : format string expected in argument 3 is not a string literal -#pragma warning(disable : 4820) // warning C4820: 'std::_Mpunct<_Elem>': '4' bytes padding added after data member 'std::_Mpunct<_Elem>::_Kseparator' -#pragma warning(disable : 5026) // warning C5026 : 'std::_Generic_error_category' : move constructor was implicitly defined as deleted -#pragma warning(disable : 5027) // warning C5027 : 'std::_Generic_error_category' : move assignment operator was implicitly defined as deleted -#endif +#include // enable the following define to show the intermediate steps in the fused-dot product // #define ALGORITHM_VERBOSE_OUTPUT @@ -30,7 +20,7 @@ #include #include -int main(int argc, char** argv) +int main() try { using namespace sw::universal; @@ -63,16 +53,16 @@ catch (char const* msg) { std::cerr << "Caught exception: " << msg << std::endl; return EXIT_FAILURE; } -catch (const sw::universal::posit_arithmetic_exception& err) { - std::cerr << "Uncaught posit arithmetic exception: " << err.what() << std::endl; +catch (const sw::universal::universal_arithmetic_exception& err) { + std::cerr << "Uncaught universal arithmetic exception: " << err.what() << std::endl; return EXIT_FAILURE; } catch (const sw::universal::quire_exception& err) { std::cerr << "Uncaught quire exception: " << err.what() << std::endl; return EXIT_FAILURE; } -catch (const sw::universal::posit_internal_exception& err) { - std::cerr << "Uncaught posit internal exception: " << err.what() << std::endl; +catch (const sw::universal::universal_internal_exception& err) { + std::cerr << "Uncaught universal internal exception: " << err.what() << std::endl; return EXIT_FAILURE; } catch (const std::runtime_error& err) { diff --git a/benchmark/reproducibility/blas/gemm.cpp b/benchmark/reproducibility/blas/gemm.cpp index 5991ba840..588c72e35 100644 --- a/benchmark/reproducibility/blas/gemm.cpp +++ b/benchmark/reproducibility/blas/gemm.cpp @@ -3,6 +3,7 @@ // Copyright (C) 2017-2023 Stillwater Supercomputing, Inc. // // This file is part of the universal numbers project, which is released under an MIT Open Source license. +#include // enable the following define to show the intermediate steps in the fused-dot product // #define ALGORITHM_VERBOSE_OUTPUT @@ -37,7 +38,7 @@ sw::universal::occurrence sw::universal::edecimal::ops; #endif -int main(int argc, char** argv) +int main() try { using namespace sw::universal::blas; @@ -60,16 +61,16 @@ catch (char const* msg) { std::cerr << "Caught exception: " << msg << std::endl; return EXIT_FAILURE; } -catch (const sw::universal::posit_arithmetic_exception& err) { - std::cerr << "Uncaught posit arithmetic exception: " << err.what() << std::endl; +catch (const sw::universal::universal_arithmetic_exception& err) { + std::cerr << "Uncaught universal arithmetic exception: " << err.what() << std::endl; return EXIT_FAILURE; } catch (const sw::universal::quire_exception& err) { std::cerr << "Uncaught quire exception: " << err.what() << std::endl; return EXIT_FAILURE; } -catch (const sw::universal::posit_internal_exception& err) { - std::cerr << "Uncaught posit internal exception: " << err.what() << std::endl; +catch (const sw::universal::universal_internal_exception& err) { + std::cerr << "Uncaught universal internal exception: " << err.what() << std::endl; return EXIT_FAILURE; } catch (const std::runtime_error& err) { diff --git a/benchmark/reproducibility/blas/matvec.cpp b/benchmark/reproducibility/blas/matvec.cpp index 3d215e2e2..4b5b98a47 100644 --- a/benchmark/reproducibility/blas/matvec.cpp +++ b/benchmark/reproducibility/blas/matvec.cpp @@ -1,8 +1,9 @@ // matvect.cpp: data flow performance measurement of mixed-precision matrix-vector product // -// Copyright (C) 2017-2021 Stillwater Supercomputing, Inc. +// Copyright (C) 2017-2023 Stillwater Supercomputing, Inc. // // This file is part of the universal numbers project, which is released under an MIT Open Source license. +#include // enable the following define to show the intermediate steps in the fused-dot product // #define ALGORITHM_VERBOSE_OUTPUT @@ -43,7 +44,7 @@ void catastrophicCancellationTest() { } } -int main(int argc, char** argv) +int main() try { catastrophicCancellationTest(); catastrophicCancellationTest(); @@ -55,16 +56,16 @@ catch (char const* msg) { std::cerr << "Caught exception: " << msg << std::endl; return EXIT_FAILURE; } -catch (const sw::universal::posit_arithmetic_exception& err) { - std::cerr << "Uncaught posit arithmetic exception: " << err.what() << std::endl; +catch (const sw::universal::universal_arithmetic_exception& err) { + std::cerr << "Uncaught universal arithmetic exception: " << err.what() << std::endl; return EXIT_FAILURE; } catch (const sw::universal::quire_exception& err) { std::cerr << "Uncaught quire exception: " << err.what() << std::endl; return EXIT_FAILURE; } -catch (const sw::universal::posit_internal_exception& err) { - std::cerr << "Uncaught posit internal exception: " << err.what() << std::endl; +catch (const sw::universal::universal_internal_exception& err) { + std::cerr << "Uncaught universal internal exception: " << err.what() << std::endl; return EXIT_FAILURE; } catch (const std::runtime_error& err) { diff --git a/docker/build_release_container.sh b/docker/build_release_container.sh index 9bf3bf397..fdabb48b6 100755 --- a/docker/build_release_container.sh +++ b/docker/build_release_container.sh @@ -5,11 +5,7 @@ # example would be to strace an executable to find its dependencies MAJOR=v3 -<<<<<<< HEAD -MINOR=72 -======= MINOR=73 ->>>>>>> upstream/v3.73 VERSION="$MAJOR.$MINOR" if [[ $# == 0 ]]; then diff --git a/docker/build_test_container.sh b/docker/build_test_container.sh index 00660d8a2..06dc5b279 100755 --- a/docker/build_test_container.sh +++ b/docker/build_test_container.sh @@ -11,11 +11,7 @@ # example would be to strace an executable to find its dependencies MAJOR=v3 -<<<<<<< HEAD -MINOR=72 -======= MINOR=73 ->>>>>>> upstream/v3.73 VERSION="$MAJOR.$MINOR" if [[ $# == 0 ]]; then diff --git a/include/universal/blas/blas_l1.hpp b/include/universal/blas/blas_l1.hpp index 7b30f90d0..9e0f7cccd 100644 --- a/include/universal/blas/blas_l1.hpp +++ b/include/universal/blas/blas_l1.hpp @@ -127,7 +127,7 @@ template size_t amax(size_t n, const Vector& x, size_t incx = 1) { size_t ix{ 0 }, index{ 0 }; auto running_max = abs(x[ix]); - for (ix = 1; ix < size(x); ix += incx) { + for (ix = 1; ix < n; ix += incx) { auto absolute = abs(x[ix]); if (absolute > running_max) { index = ix; @@ -142,7 +142,7 @@ template size_t amin(size_t n, const Vector& x, size_t incx = 1) { size_t ix{ 0 }, index{ 0 }; auto running_min = abs(x[ix]); - for (ix = 1; ix < size(x); ix += incx) { + for (ix = 1; ix < n; ix += incx) { auto absolute = abs(x[ix]); if (absolute < running_min) { index = ix; diff --git a/include/universal/blas/generators/frank.hpp b/include/universal/blas/generators/frank.hpp index 758671bef..bf9d647c8 100644 --- a/include/universal/blas/generators/frank.hpp +++ b/include/universal/blas/generators/frank.hpp @@ -11,7 +11,7 @@ namespace sw { namespace universal { namespace blas { // fill a dense (N, N) matrix with linear index values in row order template -matrix frank(int N) { +matrix frank(unsigned N) { using Matrix = matrix; // precondition tests if (N <= 0) return matrix{}; @@ -43,16 +43,16 @@ matrix frank(int N) { * .... * [ 0 0 0 .... 1 1 ] */ - for (size_t i = 0; i < size_t(N); ++i) { - for (size_t j = 0; j < size_t(N); ++j) { + for (unsigned i = 0; i < N; ++i) { + for (unsigned j = 0; j < N; ++j) { if (j + 2 <= i) { A(i, j) = Scalar(0); } else if (j + 1 == i) { - A(i, j) = Scalar(N - int(i)); + A(i, j) = Scalar(N - i); } else if (j >= i) { - A(i, j) = Scalar(N - int(j)); + A(i, j) = Scalar(N - j); } else { std::cerr << "unassigned condition " << i << " , " << j << std::endl; diff --git a/include/universal/blas/matrix.hpp b/include/universal/blas/matrix.hpp index c199b1c80..77c20d4e9 100644 --- a/include/universal/blas/matrix.hpp +++ b/include/universal/blas/matrix.hpp @@ -89,8 +89,8 @@ class matrix { matrix() : _m{ 0 }, _n{ 0 }, data(0) {} matrix(unsigned m, unsigned n) : _m{ m }, _n{ n }, data(m*n, Scalar(0.0)) { } matrix(std::initializer_list< std::initializer_list > values) { - unsigned nrows = values.size(); - unsigned ncols = values.begin()->size(); + unsigned nrows = static_cast(values.size()); + unsigned ncols = static_cast(values.begin()->size()); data.resize(nrows * ncols); unsigned r = 0; for (auto l : values) { diff --git a/include/universal/blas/operators.hpp b/include/universal/blas/operators.hpp index 508d15b31..a6ab320fa 100644 --- a/include/universal/blas/operators.hpp +++ b/include/universal/blas/operators.hpp @@ -11,7 +11,7 @@ namespace sw { namespace universal { namespace blas { // generate identity matrix template -matrix eye(size_t N) { +matrix eye(unsigned N) { matrix I(N, N); I = Scalar(1.0f); return I; diff --git a/include/universal/internal/bitblock/bitblock.hpp b/include/universal/internal/bitblock/bitblock.hpp index 3021be1b5..335388266 100644 --- a/include/universal/internal/bitblock/bitblock.hpp +++ b/include/universal/internal/bitblock/bitblock.hpp @@ -64,7 +64,7 @@ bool twosComplementLessThan(const bitblock& lhs, const bitblock& r if (lhs[nbits - 1] == 0 && rhs[nbits - 1] == 1) return false; if (lhs[nbits - 1] == 1 && rhs[nbits - 1] == 0) return true; // sign is equal, compare the remaining bits - if (nbits > 1) { + if constexpr (nbits > 1) { for (int i = static_cast(nbits) - 2; i >= 0; --i) { if (lhs[unsigned(i)] == 0 && rhs[unsigned(i)] == 1) return true; if (lhs[unsigned(i)] == 1 && rhs[unsigned(i)] == 0) return false; diff --git a/include/universal/internal/value/value.hpp b/include/universal/internal/value/value.hpp index c8dc26fb2..27b535fb7 100644 --- a/include/universal/internal/value/value.hpp +++ b/include/universal/internal/value/value.hpp @@ -990,7 +990,7 @@ void module_multiply(const value& lhs, const value& rhs, value result_fraction; - if (fbits > 0) { + if constexpr (fbits > 0) { // fractions are without hidden bit, get_fixed_point adds the hidden bit back in bitblock r1 = lhs.get_fixed_point(); bitblock r2 = rhs.get_fixed_point(); diff --git a/include/universal/number/bfloat/bfloat.hpp b/include/universal/number/bfloat/bfloat.hpp index db63fb571..5c4b17514 100644 --- a/include/universal/number/bfloat/bfloat.hpp +++ b/include/universal/number/bfloat/bfloat.hpp @@ -46,7 +46,10 @@ //////////////////////////////////////////////////////////////////////////////////////// /// INCLUDE FILES that make up the library #include +#include +#include #include +#include #include //////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/universal/number/bfloat/bfloat16_impl.hpp b/include/universal/number/bfloat/bfloat16_impl.hpp index 49a2756bd..fc86a2e5d 100644 --- a/include/universal/number/bfloat/bfloat16_impl.hpp +++ b/include/universal/number/bfloat/bfloat16_impl.hpp @@ -18,6 +18,7 @@ namespace sw { namespace universal { + // bfloat16 is Google's Brain Float type class bfloat16 { // HELPER methods @@ -228,14 +229,108 @@ class bfloat16 { constexpr void setinf(bool sign = false) noexcept { _bits = (sign ? 0xFF80u : 0x7F80u); } + constexpr void setbit(unsigned i, bool v = true) noexcept { + unsigned short bit = (1u << i); + if (v) { + _bits |= bit; + } + else { + _bits &= ~bit; + } + } constexpr void setbits(unsigned short value) noexcept { _bits = value; } - constexpr bfloat16& minpos() noexcept { _bits = 0x0080u; return *this; } + constexpr bfloat16& minpos() noexcept { _bits = 0x0001u; return *this; } constexpr bfloat16& maxpos() noexcept { _bits = 0x7F7Fu; return *this; } constexpr bfloat16& zero() noexcept { _bits = 0x0000u; return *this; } - constexpr bfloat16& minneg() noexcept { _bits = 0x8080u; return *this; } + constexpr bfloat16& minneg() noexcept { _bits = 0x8001u; return *this; } constexpr bfloat16& maxneg() noexcept { _bits = 0xFF7Fu; return *this; } + /// + /// assign the value of the string representation to the bfloat16 + /// + /// decimal scientific notation of a real number to be assigned + /// reference to this cfloat + /// Clang doesn't support constexpr yet on string manipulations, so we need to make it conditional + CONSTEXPRESSION bfloat16& assign(const std::string& str) noexcept { + clear(); + unsigned nrChars = static_cast(str.size()); + unsigned nrBits = 0; + unsigned nrDots = 0; + std::string bits; + if (nrChars > 2) { + if (str[0] == '0' && str[1] == 'b') { + for (unsigned i = 2; i < nrChars; ++i) { + char c = str[i]; + switch (c) { + case '0': + case '1': + ++nrBits; + bits += c; + break; + case '.': + ++nrDots; + bits += c; + break; + case '\'': + // consume this delimiting character + break; + default: + std::cerr << "string contained a non-standard character: " << c << '\n'; + return *this; + } + } + } + else { + std::cerr << "string must start with 0b: instead input pattern was " << str << '\n'; + return *this; + } + } + else { + std::cerr << "string is too short\n"; + return *this; + } + + if (nrBits != nbits) { + std::cerr << "number of bits in the string is " << nrBits << " and needs to be " << nbits << '\n'; + return *this; + } + if (nrDots != 2) { + std::cerr << "number of segment delimiters in string is " << nrDots << " and needs to be 2 for a cfloat<>\n"; + return *this; + } + + // assign the bits + int field{ 0 }; // three fields: sign, exponent, mantissa: fields are separated by a '.' + int nrExponentBits{ -1 }; + unsigned bit = nrBits; + for (unsigned i = 0; i < bits.size(); ++i) { + char c = bits[i]; + if (c == '.') { + ++field; + if (field == 2) { // just finished parsing exponent field: we can now check the number of exponent bits + if (nrExponentBits != es) { + std::cerr << "provided binary string representation does not contain " << es << " exponent bits. Found " << nrExponentBits << ". Reset to 0\n"; + clear(); + return *this; + } + } + } + else { + setbit(--bit, c == '1'); + } + if (field == 1) { // exponent field + ++nrExponentBits; + } + } + if (field != 2) { + std::cerr << "provided binary string did not contain three fields separated by '.': Reset to 0\n"; + clear(); + return *this; + } + return *this; + } + // selectors constexpr bool iszero() const noexcept { return _bits == 0; } constexpr bool isone() const noexcept { return (_bits & 0x7F00u); } @@ -266,6 +361,34 @@ class bfloat16 { constexpr int scale() const noexcept { int biased = static_cast((_bits & 0x7F80u) >> 7); return biased - 127; } constexpr unsigned short bits() const noexcept { return _bits; } + constexpr bool test(unsigned bitIndex) const noexcept { return at(bitIndex); } + constexpr bool at(unsigned bitIndex) const noexcept { + if (bitIndex < nbits) { + uint16_t word = _bits; + uint16_t mask = uint16_t(1ull << bitIndex); + return (word & mask); + } + return false; + } + constexpr uint8_t nibble(unsigned n) const noexcept { + if (n < 4) { + uint16_t word = _bits; + int nibbleIndexInWord = int(n); + uint16_t mask = uint16_t(0xF << (nibbleIndexInWord * 4)); + uint16_t nibblebits = uint16_t(mask & word); + return uint8_t(nibblebits >> (nibbleIndexInWord * 4)); + } + return 0; + } + constexpr uint8_t exponent() const noexcept { + uint8_t e = static_cast((_bits & 0x7f80) >> 7); + return e; + } + constexpr uint8_t fraction() const noexcept { + uint8_t f = static_cast(_bits & 0x7f); + return f; + } + protected: unsigned short _bits; @@ -294,7 +417,7 @@ inline bfloat16 abs(bfloat16 a) { // parse a bfloat16 ASCII format and make a binary bfloat16 out of it bool parse(const std::string& number, bfloat16& value) { bool bSuccess = false; - + value.zero(); return bSuccess; } diff --git a/include/universal/number/bfloat/bfloat8_fwd.hpp b/include/universal/number/bfloat/bfloat8_fwd.hpp new file mode 100644 index 000000000..6eac03f6f --- /dev/null +++ b/include/universal/number/bfloat/bfloat8_fwd.hpp @@ -0,0 +1,14 @@ +#pragma once +// bfloat8_fwd.hpp: forward definitions of the Google Brain Float number system +// +// Copyright (C) 2022-2023 Stillwater Supercomputing, Inc. +// +// This file is part of the universal numbers project, which is released under an MIT Open Source license. + +namespace sw { namespace universal { + + // forward references + class bfloat8; + bool parse(const std::string& number, bfloat8& v); + +}} // namespace sw::universal diff --git a/include/universal/number/bfloat/bfloat8_impl.hpp b/include/universal/number/bfloat/bfloat8_impl.hpp new file mode 100644 index 000000000..9487f1ca0 --- /dev/null +++ b/include/universal/number/bfloat/bfloat8_impl.hpp @@ -0,0 +1,599 @@ +#pragma once +// bfloat8_impl.hpp: definition of the Google Brain Float number system +// +// Copyright (C) 2022-2022 Stillwater Supercomputing, Inc. +// +// This file is part of the universal numbers project, which is released under an MIT Open Source license. +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace sw { namespace universal { + + +// bfloat8 is Google's Brain Float type +class bfloat8 { + // HELPER methods + template::value, SignedInt >::type> + constexpr bfloat8& convert_signed(SignedInt v) noexcept { + if (0 == v) { + setzero(); + } + else { + float f = float(v); + uint16_t pun[2]; + std::memcpy(pun, &f, 4); + _bits = pun[1]; + } + return *this; + } + template::value, UnsignedInt >::type> + constexpr bfloat8& convert_unsigned(UnsignedInt v) noexcept { + if (0 == v) { + setzero(); + } + else { + float f = float(v); + uint16_t pun[2]; + std::memcpy(pun, &f, 4); + _bits = pun[1]; + } + return *this; + } + template::value, Real >::type> + constexpr bfloat8& convert_ieee754(Real rhs) noexcept { + float f = float(rhs); + uint16_t pun[2]; + std::memcpy(pun, &f, 4); + _bits = pun[1]; + return *this; + } + template::value, Real >::type> + constexpr Real convert_to_ieee754() const noexcept { + float f; + uint16_t pun[2]; + pun[1] = _bits; + pun[0] = 0; + std::memcpy(&f, pun, 4); + return Real(f); + } +public: + static constexpr unsigned nbits = 16; + static constexpr unsigned es = 8; + + bfloat8() = default; + + constexpr bfloat8(const bfloat8&) = default; + constexpr bfloat8(bfloat8&&) = default; + + constexpr bfloat8& operator=(const bfloat8&) = default; + constexpr bfloat8& operator=(bfloat8&&) = default; + + // specific value constructor + constexpr bfloat8(const SpecificValue code) noexcept : _bits{} { + switch (code) { + case SpecificValue::infpos: + setinf(false); + break; + case SpecificValue::maxpos: + maxpos(); + break; + case SpecificValue::minpos: + minpos(); + break; + case SpecificValue::zero: + default: + zero(); + break; + case SpecificValue::minneg: + minneg(); + break; + case SpecificValue::infneg: + setinf(true); + break; + case SpecificValue::maxneg: + maxneg(); + break; + case SpecificValue::qnan: + case SpecificValue::nar: + setnan(NAN_TYPE_QUIET); + break; + case SpecificValue::snan: + setnan(NAN_TYPE_SIGNALLING); + break; + } + } + + // initializers for native types + constexpr bfloat8(signed char initial_value) noexcept : _bits{} { *this = initial_value; } + constexpr bfloat8(short initial_value) noexcept : _bits{} { *this = initial_value; } + constexpr bfloat8(int initial_value) noexcept : _bits{} { *this = initial_value; } + constexpr bfloat8(long initial_value) noexcept : _bits{} { *this = initial_value; } + constexpr bfloat8(long long initial_value) noexcept : _bits{} { *this = initial_value; } + constexpr bfloat8(char initial_value) noexcept : _bits{} { *this = initial_value; } + constexpr bfloat8(unsigned short initial_value) noexcept : _bits{} { *this = initial_value; } + constexpr bfloat8(unsigned int initial_value) noexcept : _bits{} { *this = initial_value; } + constexpr bfloat8(unsigned long initial_value) noexcept : _bits{} { *this = initial_value; } + constexpr bfloat8(unsigned long long initial_value) noexcept : _bits{} { *this = initial_value; } + constexpr bfloat8(float initial_value) noexcept : _bits{} { *this = initial_value; } + constexpr bfloat8(double initial_value) noexcept : _bits{} { *this = initial_value; } + constexpr bfloat8(long double initial_value) noexcept : _bits{} { *this = initial_value; } + + // assignment operators for native types + constexpr bfloat8& operator=(signed char rhs) noexcept { return convert_signed(rhs); } + constexpr bfloat8& operator=(short rhs) noexcept { return convert_signed(rhs); } + constexpr bfloat8& operator=(int rhs) noexcept { return convert_signed(rhs); } + constexpr bfloat8& operator=(long rhs) noexcept { return convert_signed(rhs); } + constexpr bfloat8& operator=(long long rhs) noexcept { return convert_signed(rhs); } + constexpr bfloat8& operator=(char rhs) noexcept { return convert_unsigned(rhs); } + constexpr bfloat8& operator=(unsigned short rhs) noexcept { return convert_unsigned(rhs); } + constexpr bfloat8& operator=(unsigned int rhs) noexcept { return convert_unsigned(rhs); } + constexpr bfloat8& operator=(unsigned long rhs) noexcept { return convert_unsigned(rhs); } + constexpr bfloat8& operator=(unsigned long long rhs) noexcept { return convert_unsigned(rhs); } + constexpr bfloat8& operator=(float rhs) noexcept { return convert_ieee754(rhs); } + constexpr bfloat8& operator=(double rhs) noexcept { return convert_ieee754(rhs); } + constexpr bfloat8& operator=(long 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(); } + + // prefix operators + bfloat8 operator-() const noexcept { + bfloat8 tmp; + tmp.setbits(_bits ^ 0x8000u); + return tmp; + } + + bfloat8& operator++() noexcept { + if (isneg()) { + if (_bits == 0x8001u) { // pattern 1.00.001 == minneg + _bits = 0; + } + else { + --_bits; + } + } + else { + if (_bits == 0x7FFFu) { // pattern = qnan + _bits = 0xFFFFu; // pattern = snan + } + else { + ++_bits; + } + } + return *this; + } + bfloat8 operator++(int) noexcept { + bfloat8 tmp(*this); + operator++(); + return tmp; + } + bfloat8& operator--() noexcept { + if (sign()) { + ++_bits; + } + else { + if (_bits == 0) { // pattern 0.00.000 = 0 + _bits = 0x8001u; // pattern 1.00.001 = minneg + } + else { + --_bits; + } + } + return *this; + } + bfloat8 operator--(int) noexcept { + bfloat8 tmp(*this); + operator--(); + return tmp; + } + + // arithmetic operators + bfloat8& operator+=(const bfloat8& rhs) { + *this = float(*this) + float(rhs); + return *this; + } + bfloat8& operator-=(const bfloat8& rhs) { + *this = float(*this) - float(rhs); + return *this; + } + bfloat8& operator*=(const bfloat8& rhs) { + *this = float(*this) * float(rhs); + return *this; + } + bfloat8& operator/=(const bfloat8& rhs) { + *this = float(*this) / float(rhs); + return *this; + } + + // modifiers + constexpr void clear() noexcept { _bits = 0; } + constexpr void setzero() noexcept { clear(); } + constexpr void setnan(int NaNType = NAN_TYPE_SIGNALLING) noexcept { + _bits = (NaNType == NAN_TYPE_SIGNALLING ? 0xFF81u : 0x7F81u); + } + constexpr void setinf(bool sign = false) noexcept { + _bits = (sign ? 0xFF80u : 0x7F80u); + } + constexpr void setbit(unsigned i, bool v = true) noexcept { + unsigned short bit = (1u << i); + if (v) { + _bits |= bit; + } + else { + _bits &= ~bit; + } + } + constexpr void setbits(unsigned short value) noexcept { _bits = value; } + + constexpr bfloat8& minpos() noexcept { _bits = 0x0001u; return *this; } + constexpr bfloat8& maxpos() noexcept { _bits = 0x7F7Fu; return *this; } + constexpr bfloat8& zero() noexcept { _bits = 0x0000u; return *this; } + constexpr bfloat8& minneg() noexcept { _bits = 0x8001u; return *this; } + constexpr bfloat8& maxneg() noexcept { _bits = 0xFF7Fu; return *this; } + + /// + /// assign the value of the string representation to the bfloat8 + /// + /// decimal scientific notation of a real number to be assigned + /// reference to this cfloat + /// Clang doesn't support constexpr yet on string manipulations, so we need to make it conditional + CONSTEXPRESSION bfloat8& assign(const std::string& str) noexcept { + clear(); + unsigned nrChars = static_cast(str.size()); + unsigned nrBits = 0; + unsigned nrDots = 0; + std::string bits; + if (nrChars > 2) { + if (str[0] == '0' && str[1] == 'b') { + for (unsigned i = 2; i < nrChars; ++i) { + char c = str[i]; + switch (c) { + case '0': + case '1': + ++nrBits; + bits += c; + break; + case '.': + ++nrDots; + bits += c; + break; + case '\'': + // consume this delimiting character + break; + default: + std::cerr << "string contained a non-standard character: " << c << '\n'; + return *this; + } + } + } + else { + std::cerr << "string must start with 0b: instead input pattern was " << str << '\n'; + return *this; + } + } + else { + std::cerr << "string is too short\n"; + return *this; + } + + if (nrBits != nbits) { + std::cerr << "number of bits in the string is " << nrBits << " and needs to be " << nbits << '\n'; + return *this; + } + if (nrDots != 2) { + std::cerr << "number of segment delimiters in string is " << nrDots << " and needs to be 2 for a cfloat<>\n"; + return *this; + } + + // assign the bits + int field{ 0 }; // three fields: sign, exponent, mantissa: fields are separated by a '.' + int nrExponentBits{ -1 }; + unsigned bit = nrBits; + for (unsigned i = 0; i < bits.size(); ++i) { + char c = bits[i]; + if (c == '.') { + ++field; + if (field == 2) { // just finished parsing exponent field: we can now check the number of exponent bits + if (nrExponentBits != es) { + std::cerr << "provided binary string representation does not contain " << es << " exponent bits. Found " << nrExponentBits << ". Reset to 0\n"; + clear(); + return *this; + } + } + } + else { + setbit(--bit, c == '1'); + } + if (field == 1) { // exponent field + ++nrExponentBits; + } + } + if (field != 2) { + std::cerr << "provided binary string did not contain three fields separated by '.': Reset to 0\n"; + clear(); + return *this; + } + return *this; + } + + // selectors + constexpr bool iszero() const noexcept { return _bits == 0; } + constexpr bool isone() const noexcept { return (_bits & 0x7F00u); } + constexpr bool isodd() const noexcept { return (_bits & 0x0001u); } + constexpr bool iseven() const noexcept { return !isodd(); } + constexpr bool isinteger() const noexcept { return false; } // return (floor(*this) == *this) ? true : false; } + constexpr bool ispos() const noexcept { return !(_bits & 0x8000u); } + constexpr bool isneg() const noexcept { return (_bits & 0x8000u); } + constexpr bool isnan(int NaNType = NAN_TYPE_EITHER) const noexcept { + bool negative = isneg(); + bool isNaN = (_bits & 0x7F80u) && (_bits & 0x007F); + bool isNegNaN = isNaN && negative; + bool isPosNaN = isNaN && !negative; + return (NaNType == NAN_TYPE_EITHER ? (isNegNaN || isPosNaN) : + (NaNType == NAN_TYPE_SIGNALLING ? isNegNaN : + (NaNType == NAN_TYPE_QUIET ? isPosNaN : false))); + } + constexpr bool isinf(int InfType = INF_TYPE_EITHER) const noexcept { + bool negative = isneg(); + bool isInf = (_bits & 0x7F80u); + bool isNegInf = isInf && negative; + bool isPosInf = isInf && !negative; + return (InfType == INF_TYPE_EITHER ? (isNegInf || isPosInf) : + (InfType == INF_TYPE_NEGATIVE ? isNegInf : + (InfType == INF_TYPE_POSITIVE ? isPosInf : false))); + } + constexpr bool sign() const noexcept { return isneg(); } + constexpr int scale() const noexcept { int biased = static_cast((_bits & 0x7F80u) >> 7); return biased - 127; } + constexpr unsigned short bits() const noexcept { return _bits; } + +protected: + unsigned short _bits; + +private: + + // bfloat8 - bfloat8 logic comparisons + friend bool operator==(bfloat8 lhs, bfloat8 rhs); + + // bfloat8 - literal logic comparisons + friend bool operator==(bfloat8 lhs, float rhs); + + // literal - bfloat8 logic comparisons + friend bool operator==(float lhs, bfloat8 rhs); +}; + +//////////////////////// functions ///////////////////////////////// + + +inline bfloat8 abs(bfloat8 a) { + return (a.isneg() ? -a : a); +} + + +/// stream operators + +// parse a bfloat8 ASCII format and make a binary bfloat8 out of it +bool parse(const std::string& number, bfloat8& value) { + bool bSuccess = false; + value.zero(); + return bSuccess; +} + +// generate an bfloat8 format ASCII format +inline std::ostream& operator<<(std::ostream& ostr, bfloat8 bf) { + return ostr << float(bf); +} + +// read an ASCII bfloat8 format +inline std::istream& operator>>(std::istream& istr, bfloat8& p) { + std::string txt; + istr >> txt; + if (!parse(txt, p)) { + std::cerr << "unable to parse -" << txt << "- into a bfloat8 value\n"; + } + return istr; +} + +////////////////// string operators + +std::string to_binary(bfloat8 bf, bool bNibbleMarker = false) { + std::stringstream s; + unsigned short bits = bf.bits(); + unsigned short mask = 0x8000u; + s << (bits & mask ? "0b1." : "0x0."); + mask >>= 1; + for (unsigned i = 1; i < 16; ++i) { + if (9 == i) { + s << '.'; + } + else if (bNibbleMarker && (4 == i || 8 == i || 12 == i)) { + s << '\''; + } + + s << (bits & mask ? '1' : '0'); + mask >>= 1; + } + return s.str(); +} + +////////////////////////////////////////////////////////////////////////////////////////////////////// +// bfloat8 - bfloat8 binary logic operators + +// equal: precondition is that the storage is properly nulled in all arithmetic paths +inline bool operator==(bfloat8 lhs, bfloat8 rhs) { + // NaN != NaN + if (lhs.isnan() || rhs.isnan()) return false; + return lhs._bits == rhs._bits; +} + +inline bool operator!=(bfloat8 lhs, bfloat8 rhs) { + return !operator==(lhs, rhs); +} + +inline bool operator< (bfloat8 lhs, bfloat8 rhs) { + return (float(lhs) - float(rhs)) > 0; +} + +inline bool operator> (bfloat8 lhs, bfloat8 rhs) { + return operator< (rhs, lhs); +} + +inline bool operator<=(bfloat8 lhs, bfloat8 rhs) { + return operator< (lhs, rhs) || operator==(lhs, rhs); +} + +inline bool operator>=(bfloat8 lhs, bfloat8 rhs) { + return !operator< (lhs, rhs); +} + +////////////////////////////////////////////////////////////////////////////////////////////////////// +// bfloat8 - literal binary logic operators +// equal: precondition is that the byte-storage is properly nulled in all arithmetic paths + +inline bool operator==(const bfloat8& lhs, float rhs) { + return operator==(lhs, bfloat8(rhs)); +} + +inline bool operator!=(const bfloat8& lhs, float rhs) { + return !operator==(lhs, bfloat8(rhs)); +} + +inline bool operator< (const bfloat8& lhs, float rhs) { + return operator<(lhs, bfloat8(rhs)); +} + +inline bool operator> (const bfloat8& lhs, float rhs) { + return operator< (bfloat8(rhs), lhs); +} + +inline bool operator<=(const bfloat8& lhs, float rhs) { + return operator< (lhs, bfloat8(rhs)) || operator==(lhs, bfloat8(rhs)); +} + +inline bool operator>=(const bfloat8& lhs, float rhs) { + return !operator< (lhs, bfloat8(rhs)); +} + +////////////////////////////////////////////////////////////////////////////////////////////////////// +// literal - bfloat8 binary logic operators +// precondition is that the byte-storage is properly nulled in all arithmetic paths + + +inline bool operator==(float lhs, const bfloat8& rhs) { + return operator==(bfloat8(lhs), rhs); +} + +inline bool operator!=(float lhs, const bfloat8& rhs) { + return !operator==(bfloat8(lhs), rhs); +} + +inline bool operator< (float lhs, const bfloat8& rhs) { + return operator<(bfloat8(lhs), rhs); +} + +inline bool operator> (float lhs, const bfloat8& rhs) { + return operator< (rhs, bfloat8(lhs)); +} + +inline bool operator<=(float lhs, const bfloat8& rhs) { + return operator< (bfloat8(lhs), rhs) || operator==(bfloat8(lhs), rhs); +} + +inline bool operator>=(float lhs, const bfloat8& rhs) { + return !operator< (bfloat8(lhs), rhs); +} + +////////////////////////////////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////////////////////////////////// +// bfloat8 - bfloat8 binary arithmetic operators +// BINARY ADDITION + +inline bfloat8 operator+(bfloat8 lhs, bfloat8 rhs) { + bfloat8 sum = lhs; + sum += rhs; + return sum; +} +// BINARY SUBTRACTION + +inline bfloat8 operator-(bfloat8 lhs, bfloat8 rhs) { + bfloat8 diff = lhs; + diff -= rhs; + return diff; +} +// BINARY MULTIPLICATION + +inline bfloat8 operator*(bfloat8 lhs, bfloat8 rhs) { + bfloat8 mul = lhs; + mul *= rhs; + return mul; +} +// BINARY DIVISION + +inline bfloat8 operator/(bfloat8 lhs, bfloat8 rhs) { + bfloat8 ratio = lhs; + ratio /= rhs; + return ratio; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////// +// bfloat8 - literal binary arithmetic operators +// BINARY ADDITION + +inline bfloat8 operator+(bfloat8 lhs, float rhs) { + return operator+(lhs, bfloat8(rhs)); +} +// BINARY SUBTRACTION + +inline bfloat8 operator-(bfloat8 lhs, float rhs) { + return operator-(lhs, bfloat8(rhs)); +} +// BINARY MULTIPLICATION + +inline bfloat8 operator*(bfloat8 lhs, float rhs) { + return operator*(lhs, bfloat8(rhs)); +} +// BINARY DIVISION + +inline bfloat8 operator/(bfloat8 lhs, float rhs) { + return operator/(lhs, bfloat8(rhs)); +} + +////////////////////////////////////////////////////////////////////////////////////////////////////// +// literal - bfloat8 binary arithmetic operators +// BINARY ADDITION + +inline bfloat8 operator+(float lhs, bfloat8 rhs) { + return operator+(bfloat8(lhs), rhs); +} +// BINARY SUBTRACTION + +inline bfloat8 operator-(float lhs, bfloat8 rhs) { + return operator-(bfloat8(lhs), rhs); +} +// BINARY MULTIPLICATION + +inline bfloat8 operator*(float lhs, bfloat8 rhs) { + return operator*(bfloat8(lhs), rhs); +} +// BINARY DIVISION + +inline bfloat8 operator/(float lhs, bfloat8 rhs) { + return operator/(bfloat8(lhs), rhs); +} + +}} // namespace sw::universal diff --git a/include/universal/number/bfloat/manipulators.hpp b/include/universal/number/bfloat/manipulators.hpp index 86509c50f..d4b4d5eb8 100644 --- a/include/universal/number/bfloat/manipulators.hpp +++ b/include/universal/number/bfloat/manipulators.hpp @@ -1,16 +1,122 @@ #pragma once // manipulators.hpp: definition of manipulation functions for bfloat // -// Copyright (C) 2022-2022 Stillwater Supercomputing, Inc. +// Copyright (C) 2022-2023 Stillwater Supercomputing, Inc. // // This file is part of the universal numbers project, which is released under an MIT Open Source license. #include +#include +#include +#include +// pull in the color printing for shells utility +#include namespace sw { namespace universal { -// Generate a type tag for bfloat16 -std::string type_tag(const bfloat16& = {}) { - return std::string("bfloat16"); -} + // Generate a type tag for bfloat8 + std::string type_tag(const bfloat8 & = {}) { + return std::string("bfloat16"); + } + + // Generate a type tag for bfloat16 + std::string type_tag(const bfloat16& = {}) { + return std::string("bfloat16"); + } + + // Generate a type field descriptor for this cfloat + template, bool> = true + > + inline std::string type_field(const BfloatType & = {}) { + std::stringstream s; + + // unsigned nbits = BfloatType::nbits; // total bits + unsigned ebits = BfloatType::es; // exponent bits + unsigned fbits = BfloatType::fbits; // integer bits + s << "fields(s:1|e:" << ebits << "|m:" << fbits << ')'; + return s.str(); + } + + // Generate a type field descriptor for this cfloat + template, bool> = true + > + inline std::string type_field(const BfloatType & = {}) { + std::stringstream s; + + // unsigned nbits = BfloatType::nbits; // total bits + unsigned ebits = BfloatType::es; // exponent bits + unsigned fbits = BfloatType::fbits; // integer bits + s << "fields(s:1|e:" << ebits << "|m:" << fbits << ')'; + return s.str(); + } + + // generate a binary string for bfloat16 + inline std::string to_hex(const bfloat16& v, bool nibbleMarker = false, bool hexPrefix = true) { + constexpr unsigned nbits = 16; + char hexChar[16] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', + }; + std::stringstream s; + if (hexPrefix) s << "0x" << std::hex; + int nrNibbles = int(1ull + ((nbits - 1ull) >> 2ull)); + for (int n = nrNibbles - 1; n >= 0; --n) { + uint8_t nibble = v.nibble(unsigned(n)); + s << hexChar[nibble]; + if (nibbleMarker && n > 0 && (n % 4) == 0) s << '\''; + } + return s.str(); + } + + // generate a bfloat format ASCII hex format nbits.esxNN...NNa + inline std::string hex_print(const bfloat16& c) { + constexpr unsigned nbits = 16; + constexpr unsigned es = 8; + std::stringstream s; + s << nbits << '.' << es << 'x' << to_hex(c) << 'c'; + return s.str(); + } + + // generate a binary, color-coded representation of the bfloat16 + std::string color_print(const bfloat16& r, bool nibbleMarker = false) { + using Real = bfloat16; + constexpr unsigned es = 8; + constexpr unsigned fbits = 7; + std::stringstream s; + bool sign{ false }; + blockbinary e{0}; + blockbinary f{0}; + sign = r.sign(); + e = r.exponent(); + f = r.fraction(); + + Color red(ColorCode::FG_RED); + Color yellow(ColorCode::FG_YELLOW); + Color blue(ColorCode::FG_BLUE); + Color magenta(ColorCode::FG_MAGENTA); + Color cyan(ColorCode::FG_CYAN); + Color white(ColorCode::FG_WHITE); + Color def(ColorCode::FG_DEFAULT); + + // sign bit + s << red << (sign ? '1' : '0'); + + // exponent bits + for (int i = int(es) - 1; i >= 0; --i) { + s << cyan << (e.test(static_cast(i)) ? '1' : '0'); + if ((i - es) > 0 && ((i - es) % 4) == 0 && nibbleMarker) s << yellow << '\''; + } + + // fraction bits + for (int i = int(fbits) - 1; i >= 0; --i) { + s << magenta << (f.test(static_cast(i)) ? '1' : '0'); + if (i > 0 && (i % 4) == 0 && nibbleMarker) s << yellow << '\''; + } + + s << def; + return s.str(); + } + }} // namespace sw::universal diff --git a/include/universal/number/cfloat/cfloat.hpp b/include/universal/number/cfloat/cfloat.hpp index 1a1c5b85d..5a14b1564 100644 --- a/include/universal/number/cfloat/cfloat.hpp +++ b/include/universal/number/cfloat/cfloat.hpp @@ -94,7 +94,7 @@ using fp256 = octo; // DL // Google brain float -using bfloat_t = cfloat<16, 8, std::uint16_t, false, false, false>; +using bfloat_t = cfloat<16, 8, std::uint16_t, true, false, false>; using msfp8 = cfloat<8, 2, std::uint8_t, false, false, false>; using msfp9 = cfloat<9, 3, std::uint16_t, false, false, false>; using amd24 = cfloat<24, 8, std::uint32_t, false, false, false>; diff --git a/include/universal/number/faithful/faithful_impl.hpp b/include/universal/number/faithful/faithful_impl.hpp index 9d549934f..fb77cca9c 100644 --- a/include/universal/number/faithful/faithful_impl.hpp +++ b/include/universal/number/faithful/faithful_impl.hpp @@ -9,7 +9,7 @@ #include #include -#include +#include namespace sw { namespace universal { diff --git a/include/universal/number/posit/exponent.hpp b/include/universal/number/posit/exponent.hpp index e89c8a541..0b19a1270 100644 --- a/include/universal/number/posit/exponent.hpp +++ b/include/universal/number/posit/exponent.hpp @@ -58,7 +58,7 @@ class exponent { _Bits.reset(); // start of exponent is nbits - (sign_bit + regime_bits) int msb = int(static_cast(nbits) - 1ull - (1ull + nrRegimeBits)); - if (es > 0) { + if constexpr (es > 0) { unsigned nrExponentBits = 0; bitblock _exp; if (msb >= 0 && es > 0) { diff --git a/include/universal/number/posit/fraction.hpp b/include/universal/number/posit/fraction.hpp index 17da6527b..64ef6d426 100644 --- a/include/universal/number/posit/fraction.hpp +++ b/include/universal/number/posit/fraction.hpp @@ -240,7 +240,7 @@ class fraction { template inline std::ostream& operator<<(std::ostream& ostr, const fraction& f) { unsigned nrOfFractionBitsProcessed = 0; - if (nfbits > 0) { + if constexpr (nfbits > 0) { int upperbound = int(nfbits) - 1; for (int i = upperbound; i >= 0; --i) { if (f._NrOfBits > ++nrOfFractionBitsProcessed) { diff --git a/include/universal/number/posit/posit.hpp b/include/universal/number/posit/posit.hpp index f9af4407e..fd253a56e 100644 --- a/include/universal/number/posit/posit.hpp +++ b/include/universal/number/posit/posit.hpp @@ -39,10 +39,12 @@ #define VALUE_THROW_ARITHMETIC_EXCEPTION 0 #define BITBLOCK_THROW_ARITHMETIC_EXCEPTION 0 #else -// for the value<> class assume the same behavior as requested for posits +// for the composite value<> class assume the same behavior as requested for posits #define VALUE_THROW_ARITHMETIC_EXCEPTION POSIT_THROW_ARITHMETIC_EXCEPTION +#if !defined(BITBLOCK_THROW_ARITHMETIC_EXCEPTION) #define BITBLOCK_THROW_ARITHMETIC_EXCEPTION POSIT_THROW_ARITHMETIC_EXCEPTION #endif +#endif //////////////////////////////////////////////////////////////////////////////////////// /// END OF BEHAVIOR SWITCHES /// diff --git a/include/universal/number/posit/posit_impl.hpp b/include/universal/number/posit/posit_impl.hpp index 7f634eb20..6b1bd3185 100644 --- a/include/universal/number/posit/posit_impl.hpp +++ b/include/universal/number/posit/posit_impl.hpp @@ -170,7 +170,7 @@ void extract_fields(const bitblock& raw_bits, bool& _sign, regime(nbits - 1ul - (1ul + nrRegimeBits)); unsigned nrExponentBits = 0; - if (es > 0) { + if constexpr (es > 0) { bitblock _exp; if (msb >= 0 && es > 0) { nrExponentBits = (msb >= static_cast(es - 1ull)) ? es : static_cast(msb + 1ll); @@ -195,7 +195,7 @@ void extract_fields(const bitblock& raw_bits, bool& _sign, regime= 0) { for (int64_t i = msb; i >= 0; --i) { uint64_t fbit = fbits - 1ull - (static_cast(msb) - i); - _frac[fbit] = tmp[i]; + _frac[fbit] = tmp[static_cast(i)]; } } _fraction.set(_frac, nrFractionBits); diff --git a/include/universal/analysis/README.md b/include/universal/numerics/README.md similarity index 100% rename from include/universal/analysis/README.md rename to include/universal/numerics/README.md diff --git a/include/universal/analysis/eft.hpp b/include/universal/numerics/eft.hpp similarity index 69% rename from include/universal/analysis/eft.hpp rename to include/universal/numerics/eft.hpp index f5e0db0b8..0ddce8d35 100644 --- a/include/universal/analysis/eft.hpp +++ b/include/universal/numerics/eft.hpp @@ -5,6 +5,6 @@ // // This file is part of the universal numbers project, which is released under an MIT Open Source license. -#include -#include -#include +#include +#include +#include diff --git a/include/universal/analysis/twodiv.hpp b/include/universal/numerics/twodiv.hpp similarity index 100% rename from include/universal/analysis/twodiv.hpp rename to include/universal/numerics/twodiv.hpp diff --git a/include/universal/analysis/twoprod.hpp b/include/universal/numerics/twoprod.hpp similarity index 100% rename from include/universal/analysis/twoprod.hpp rename to include/universal/numerics/twoprod.hpp diff --git a/include/universal/analysis/twosum.hpp b/include/universal/numerics/twosum.hpp similarity index 99% rename from include/universal/analysis/twosum.hpp rename to include/universal/numerics/twosum.hpp index 8a970f7b3..8a412060c 100644 --- a/include/universal/analysis/twosum.hpp +++ b/include/universal/numerics/twosum.hpp @@ -4,7 +4,6 @@ // Copyright (C) 2020-2023 Stillwater Supercomputing, Inc. // // This file is part of the universal numbers project, which is released under an MIT Open Source license. -#include namespace sw { namespace universal { diff --git a/include/universal/traits/bfloat16_traits.hpp b/include/universal/traits/bfloat16_traits.hpp new file mode 100644 index 000000000..f76fe7335 --- /dev/null +++ b/include/universal/traits/bfloat16_traits.hpp @@ -0,0 +1,31 @@ +#pragma once +// bfloat16_traits.hpp : traits for the bfloat16 number systems +// +// Copyright (C) 2022-2023 Stillwater Supercomputing, Inc. +// +// This file is part of the universal numbers project, which is released under an MIT Open Source license. +#include + +namespace sw { namespace universal { + + /////////////////////////////////////////////////////////////////////////// + // define a trait for bfloat16 type + template + struct is_bfloat16_trait + : false_type + { + }; + + template<> + struct is_bfloat16_trait< sw::universal::bfloat16 > + : true_type + { + }; + + template + constexpr bool is_bfloat16 = is_bfloat16_trait<_Ty>::value; + + template + using enable_if_bfloat16 = std::enable_if_t, Type>; + +}} // namespace sw::universal diff --git a/include/universal/traits/bfloat8_traits.hpp b/include/universal/traits/bfloat8_traits.hpp new file mode 100644 index 000000000..2fad23ad9 --- /dev/null +++ b/include/universal/traits/bfloat8_traits.hpp @@ -0,0 +1,31 @@ +#pragma once +// bfloat8_traits.hpp : traits for the bfloat8 number systems +// +// Copyright (C) 2022-2023 Stillwater Supercomputing, Inc. +// +// This file is part of the universal numbers project, which is released under an MIT Open Source license. +#include + +namespace sw { namespace universal { + + /////////////////////////////////////////////////////////////////////////// + // define a trait for bfloat8 type + template + struct is_bfloat8_trait + : false_type + { + }; + + template<> + struct is_bfloat8_trait< sw::universal::bfloat8 > + : true_type + { + }; + + template + constexpr bool is_bfloat8 = is_bfloat8_trait<_Ty>::value; + + template + using enable_if_bfloat8 = std::enable_if_t, Type>; + +}} // namespace sw::universal diff --git a/numeric/faithful/accurate_sum_and_dot.cpp b/numeric/faithful/accurate_sum_and_dot.cpp index c2386fef9..7b02da9d5 100644 --- a/numeric/faithful/accurate_sum_and_dot.cpp +++ b/numeric/faithful/accurate_sum_and_dot.cpp @@ -9,13 +9,13 @@ #include #include #include -#include +#include template -int DemonstrateCascadeSum() -{ +int DemonstrateCascadeSum(size_t N = 10) { using namespace sw::universal; - constexpr size_t N = 10; + + std::cout << "+------------- cascade sum --------------+\n"; std::vector v(N); v[0] = 0.5f + std::numeric_limits::epsilon() / 2.0f; v[1] = 1.0f; @@ -63,7 +63,6 @@ int main() try { using namespace sw::universal; - // preserve the existing ostream precision auto precision = std::cout.precision(); std::cout << std::setprecision(12); diff --git a/numeric/faithful/arithmetic.cpp b/numeric/faithful/arithmetic.cpp index 6832375b6..b40328a70 100644 --- a/numeric/faithful/arithmetic.cpp +++ b/numeric/faithful/arithmetic.cpp @@ -6,7 +6,8 @@ #include #include #include -#include +#include +#include template void traceTwoSum(const Scalar& a, const Scalar& b, Scalar& s, Scalar& r) { @@ -41,6 +42,33 @@ void traceCascadingSum(const std::vector& v, Scalar& s, Scalar& r) { s = p; } +template +inline sw::universal::cfloat +traceFma(sw::universal::cfloat x, + sw::universal::cfloat y, + sw::universal::cfloat z) { + using namespace sw::universal; + cfloat fused{ 0 }; + constexpr unsigned FBITS = cfloat::fbits; + constexpr unsigned EXTRA_FBITS = FBITS + 2; + constexpr unsigned EXTENDED_PRECISION = nbits + EXTRA_FBITS; + // the C++ fma spec indicates that the x*y+z is evaluated in 'infinite' precision + // with only a single rounding event. The minimum finite precision that would behave like this + // is the precision where the product x*y does not need to be rounded, which will + // need at least 2*(fbits+1) mantissa bits to capture all bits that can be + // generated by the product. + + // TODO: looks like there is a bug in decorated constructor preciseX(x) does NOT yield the same value as preciseX{double(x)} + cfloat preciseX{double(x)}, preciseY{ double(y) }, preciseZ{ double(z) }; + ReportValue(preciseX, "extended precision x"); + ReportValue(preciseY, "extended precision y"); + ReportValue(preciseZ, "extended precision z"); + cfloat product = preciseX * preciseY; + ReportValue(product, "extended precision p"); + fused = product + preciseZ; + return fused; +} + template void CompensatedEvaluation() { using namespace sw::universal; @@ -126,6 +154,19 @@ try { CompensatedEvaluation >(); + std::cout << "\n+-------------- twoprod -------------+\n"; + { + single a, b, c, err; + a = 1.0e1f; + b = 1.0f + std::numeric_limits::epsilon(); + twoProd(a, b, c, err); + std::cout << a << " * " << b << " = " << c << " (+-" << err << ")\n"; + std::cout << to_binary(a) << " * " << to_binary(b) << " = " << to_binary(c) << " (+-" << err << ")\n"; + single product = a * b; + err = traceFma(a, b, -product); + std::cout << err << '\n'; + } + // restore the previous ostream precision std::cout << std::setprecision(precision); diff --git a/static/bfloat/api/api.cpp b/static/bfloat/api/api.cpp index 777b1ca35..e4aafdb82 100644 --- a/static/bfloat/api/api.cpp +++ b/static/bfloat/api/api.cpp @@ -25,7 +25,7 @@ try { a = 1.0f; std::cout << to_binary(a) << " : " << a << '\n'; } -/* + // important behavioral traits { using TestType = bfloat16; @@ -44,25 +44,23 @@ try { // report on the dynamic range of some standard configurations std::cout << "+--------- Dynamic ranges of standard cfloat configurations --------+\n"; { - // quarter, half, single, duble, quad, and octo precision IEEE-754 style floating-point - std::cout << "quarter precision: " << cfloat_range() << '\n'; - std::cout << "half precision: " << cfloat_range() << '\n'; - std::cout << "single precision: " << cfloat_range() << '\n'; - std::cout << "double precision: " << cfloat_range() << '\n'; - std::cout << "---\n"; - bfloat16 bf; // uninitialized - bf.setbits(0x01); // smallest subnormal - std::cout << "minpos bfloat16 : " << to_binary(bf) << " : " << bf << '\n'; - bf.setbits(0x5f); // max normal - std::cout << "maxnorm bfloat16 : " << to_binary(bf) << " : " << bf << '\n'; - bf.setbits(0x7d); // max supernormal - std::cout << "maxpos bfloat16 : " << to_binary(bf) << " : " << bf << '\n'; - bf.minpos(); - std::cout << "minpos bfloat16 : " << to_binary(bf) << " : " << bf << '\n'; + bf.maxpos(); - std::cout << "maxpos bfloat16 : " << to_binary(bf) << " : " << bf << '\n'; + std::cout << "maxpos bfloat16 : " << to_binary(bf) << " : " << bf << '\n'; + bf.setbits(0x0080); // positive min normal + std::cout << "minnorm bfloat16 : " << to_binary(bf) << " : " << bf << '\n'; + bf.minpos(); + std::cout << "minpos bfloat16 : " << to_binary(bf) << " : " << bf << '\n'; + bf.zero(); + std::cout << "zero : " << to_binary(bf) << " : " << bf << '\n'; + bf.minneg(); + std::cout << "minneg bfloat16 : " << to_binary(bf) << " : " << bf << '\n'; + bf.setbits(0x8080); // negative min normal + std::cout << "minnegnorm : " << to_binary(bf) << " : " << bf << '\n'; + bf.maxneg(); + std::cout << "maxneg bfloat16 : " << to_binary(bf) << " : " << bf << '\n'; std::cout << "---\n"; } @@ -113,19 +111,25 @@ try { a.setbits(0x0000); std::cout << to_binary(a) << " : " << a << '\n'; + a.setbit(8); + std::cout << to_binary(a) << " : " << a << " : set bit 8 assuming 0-based" << '\n'; + a.setbits(0xffff); + a.setbit(8, false); + std::cout << to_binary(a) << " : " << a << " : reset bit 8" << '\n'; + a.setbits(0xAAAA); std::cout << to_binary(a) << " : " << a << '\n'; - a.assign(std::string("0b1.01010.1010'1010'10")); + a.assign(std::string("0b1.0101'0101.0101'010")); std::cout << to_binary(a) << " : " << a << '\n'; - a.assign(std::string("0b1.01010.10'1010'1010")); + a.assign(std::string("0b0.1010'1010.1010'101")); std::cout << to_binary(a) << " : " << a << '\n'; } std::cout << "+--------- set specific values of interest --------+\n"; { - bfloat16 a; // uninitialized + bfloat16 a{ 0 }; // initialized std::cout << "maxpos : " << a.maxpos() << " : " << scale(a) << '\n'; std::cout << "minpos : " << a.minpos() << " : " << scale(a) << '\n'; std::cout << "zero : " << a.zero() << " : " << scale(a) << '\n'; @@ -134,29 +138,32 @@ try { std::cout << dynamic_range() << std::endl; } - std::cout << "+--------- cfloat<16, 5, uint32_t, hasSubnormals, noSupernormals, notSaturating> half-precision subnormals --------+\n"; + /* reference + std::cout << "+--------- cfloat<16, 8, uint16_t, hasSubnormals, noSupernormals, notSaturating> --------+\n"; { constexpr size_t nbits = 16; - constexpr size_t es = 5; - using BlockType = uint32_t; + constexpr size_t es = 8; + using BlockType = uint16_t; using Cfloat = cfloat; constexpr size_t fbits = Cfloat::fbits; Cfloat a, b; // uninitialized - // enumerate the subnormals - uint32_t pattern = 1ul; std::streamsize precision = std::cout.precision(); + //std::cout << std::setprecision(3); + //std::cout << std::fixed; std::cout << std::setw(nbits) << "binary" << " : " << std::setw(nbits) << "native" << " : " << std::setw(nbits) << "conversion\n"; -// std::cout << std::setprecision(3); - std::cout << std::fixed; + + // enumerate the subnormals + uint16_t pattern = 0x1ul; + for (unsigned i = 0; i < fbits; ++i) { a.setbits(pattern); std::cout << color_print(a) << " : " << std::setw(nbits) << a << " : " << std::setw(nbits) << float(a) << '\n'; pattern <<= 1; } // enumerate the normals - a.setbits(0x0400); - for (size_t i = 0; i < 30; ++i) { + a.setbits(0x0080u); + for (size_t i = 0; i < 254; ++i) { std::cout << color_print(a) << " : " << std::setw(nbits) << a << " : " << std::setw(nbits) << float(a) << " + 1ULP "; b = a; ++b; std::cout << color_print(b) << " : " << std::setw(nbits) << b << " : " << std::setw(nbits) << float(b) << '\n'; @@ -165,38 +172,37 @@ try { std::cout << std::setprecision(precision); std::cout << std::scientific; } + */ std::cout << "+--------- bfloat16 --------+\n"; { - float subnormal = std::nextafter(0.0f, 1.0f); using Bfloat = bfloat16; - Bfloat a; // uninitialized + constexpr unsigned nbits = 16; + //constexpr unsigned es = 8; + constexpr unsigned fbits = 7; + Bfloat a, b; // uninitialized - uint16_t pattern = 0x0001ul; - for (unsigned i = 0; i < 8; ++i) { + std::streamsize precision = std::cout.precision(); + //std::cout << std::setprecision(3); + //std::cout << std::fixed; + std::cout << std::setw(nbits) << "binary" << " : " << std::setw(nbits) << "native" << " : " << std::setw(nbits) << "conversion\n"; + + // enumerate the subnormals + uint16_t pattern = 0x1ul; + for (unsigned i = 0; i < fbits; ++i) { a.setbits(pattern); - std::cout << to_binary(a) << " " << a << ": "; + std::cout << color_print(a) << " : " << std::setw(nbits) << a << " : " << std::setw(nbits) << float(a) << '\n'; pattern <<= 1; - std::cout << color_print(subnormal) << " : " << subnormal << std::endl; - subnormal *= 2.0f; - - if (i < 8) { // the last iteration is a normal encoding -// constexpr bool isNormal = false; -// int scale_offset = static_cast(a.significant(significant, isNormal)); // significant will be in leading 1 format, so not interesting unless you are doing arithmetic -// int check = a.MIN_EXP_NORMAL - scale_offset; -// std::cout << a.MIN_EXP_NORMAL << " - " << scale_offset << " = (" << check << ") should be equal to " << a.scale() << std::endl; - } } - } - - std::cout << "+--------- Subnormal exponent values --------+\n"; - { - // we are not using element [0] as es = 0 is not supported in the cfloat spec - int exponents[] = { - 0, 1, 0, -2, -6, -14, -30, -62, -126, -254, -510, -1022 - }; - for (int i = 1; i < 12; ++i) { - std::cout << "es = " << i << " = " << exponents[i] << " " << std::setprecision(17) << subnormal_exponent[i] << std::endl; + // enumerate the normals + a.setbits(0x0080u); + for (size_t i = 0; i < 254; ++i) { + std::cout << color_print(a) << " : " << std::setw(nbits) << a << " : " << std::setw(nbits) << float(a) << " + 1ULP "; + b = a; ++b; + std::cout << color_print(b) << " : " << std::setw(nbits) << b << " : " << std::setw(nbits) << float(b) << '\n'; + a *= 2; } + std::cout << std::setprecision(precision); + std::cout << std::scientific; } std::cout << "+--------- special value properties bfloat16 vs IEEE754 --------+\n"; @@ -213,7 +219,7 @@ try { } bfloat16 a(fa); - if ((a < 0.0f && a > 0.0f && a != 0.0f) || a.isneg()) { + if ((a < 0.0f && a > 0.0f && a != 0.0f)) { std::cout << "bfloat16 is incorrectly implemented\n"; ++nrOfFailedTestCases; } @@ -223,27 +229,25 @@ try { } { - using cfloat = sw::universal::cfloat<32, 8, uint32_t, true, false, false>; + std::cout << "bfloat(INFINITY): " << bfloat16(INFINITY) << "\n"; + std::cout << "bfloat(-INFINITY): " << bfloat16(-INFINITY) << "\n"; - std::cout << "cfloat(INFINITY): " << cfloat(INFINITY) << "\n"; - std::cout << "cfloat(-INFINITY): " << cfloat(-INFINITY) << "\n"; - - std::cout << "cfloat(std::numeric_limits::infinity()) : " << cfloat(std::numeric_limits::infinity()) << "\n"; - std::cout << "cfloat(-std::numeric_limits::infinity()) : " << cfloat(-std::numeric_limits::infinity()) << "\n"; + std::cout << "bfloat(std::numeric_limits::infinity()) : " << bfloat16(std::numeric_limits::infinity()) << "\n"; + std::cout << "bfloat(-std::numeric_limits::infinity()) : " << bfloat16(-std::numeric_limits::infinity()) << "\n"; std::cout << " 2 * std::numeric_limits::infinity() : " << 2 * std::numeric_limits::infinity() << "\n"; - std::cout << " 2 * std::numeric_limits::infinity() : " << 2 * std::numeric_limits::infinity() << "\n"; - std::cout << "-2 * std::numeric_limits::infinity() : " << -2 * std::numeric_limits::infinity() << "\n"; + std::cout << " 2 * std::numeric_limits::infinity() : " << 2 * std::numeric_limits::infinity() << "\n"; + std::cout << "-2 * std::numeric_limits::infinity() : " << -2 * std::numeric_limits::infinity() << "\n"; - std::cout << "sw::universal::nextafter(cfloat(0), std::numeric_limits::infinity()) : " << sw::universal::nextafter(cfloat(-0), std::numeric_limits::infinity()) << "\n"; + std::cout << "sw::universal::nextafter(bfloat16(0), std::numeric_limits::infinity()) : " << sw::universal::nextafter(bfloat16(-0), std::numeric_limits::infinity()) << "\n"; std::cout << "std::nextafter(float(0), std::numeric_limits::infinity()) : " << std::nextafter(float(-0), std::numeric_limits::infinity()) << "\n"; - std::cout << "sw::universal::nextafter(cfloat(0), -std::numeric_limits::infinity()) : " << sw::universal::nextafter(cfloat(0), -std::numeric_limits::infinity()) << "\n"; + std::cout << "sw::universal::nextafter(bfloat16(0), -std::numeric_limits::infinity()) : " << sw::universal::nextafter(bfloat16(0), -std::numeric_limits::infinity()) << "\n"; std::cout << "std::nextafter(float(0), -std::numeric_limits::infinity()) : " << std::nextafter(float(0), -std::numeric_limits::infinity()) << "\n"; - std::cout << "cfloat(std::numeric_limits::signaling_NaN()).isnan(sw::universal::NAN_TYPE_QUIET) : " << cfloat(std::numeric_limits::signaling_NaN()).isnan(sw::universal::NAN_TYPE_QUIET) << "\n"; - std::cout << "cfloat(std::numeric_limits::signaling_NaN()).isnan(sw::universal::NAN_TYPE_SIGNALLING) : " << cfloat(std::numeric_limits::signaling_NaN()).isnan(sw::universal::NAN_TYPE_SIGNALLING) << "\n"; + std::cout << "cfloat(std::numeric_limits::signaling_NaN()).isnan(sw::universal::NAN_TYPE_QUIET) : " << bfloat16(std::numeric_limits::signaling_NaN()).isnan(sw::universal::NAN_TYPE_QUIET) << "\n"; + std::cout << "cfloat(std::numeric_limits::signaling_NaN()).isnan(sw::universal::NAN_TYPE_SIGNALLING) : " << bfloat16(std::numeric_limits::signaling_NaN()).isnan(sw::universal::NAN_TYPE_SIGNALLING) << "\n"; } -*/ + ReportTestSuiteResults(test_suite, nrOfFailedTestCases); return (nrOfFailedTestCases > 0 ? EXIT_FAILURE : EXIT_SUCCESS); } diff --git a/static/bfloat/api/attributes.cpp b/static/bfloat/api/attributes.cpp index cff56799c..ed41ce03a 100644 --- a/static/bfloat/api/attributes.cpp +++ b/static/bfloat/api/attributes.cpp @@ -71,8 +71,8 @@ try { { std::cout << "Number traits\n"; - numberTraits< bfloat_t >(std::cout); // FP32 - numberTraits< bfloat16 >(std::cout); // IEEE-754 + numberTraits< bfloat_t >(std::cout); // cfloat emulation + numberTraits< bfloat16 >(std::cout); // fp32 IEEE-754 emulation std::cout << '\n'; } @@ -82,7 +82,7 @@ try { } { std::cout << "Comparitive Number traits\n"; - compareNumberTraits< cfloat<8, 2>, cfloat<8, 4> >(std::cout); + compareNumberTraits< bfloat_t, bfloat16 >(std::cout); std::cout << '\n'; }