Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
cmake_minimum_required(VERSION 3.16)
project(zmij CXX)

set(ZMIJ_STANDARD cxx_std_14)
set(ZMIJ_STANDARD cxx_std_23)

add_library(zmij zmij.cc zmij.h)
target_include_directories(zmij PUBLIC .)
target_compile_features(zmij PRIVATE ${ZMIJ_STANDARD})

add_executable(example example.cc)
target_link_libraries(example zmij)
target_compile_features(example PRIVATE ${ZMIJ_STANDARD})

set(CMAKE_CTEST_ARGUMENTS "--output-on-failure")
enable_testing()
Expand Down
14 changes: 12 additions & 2 deletions example.cc
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
#include <stdio.h>
#include <stdfloat>

#include "zmij.h"

int main() {
char buf[zmij::double_buffer_size + 1];
auto end = zmij::write(buf, sizeof(buf), 5.0507837461e-27);
size_t size = zmij::double_buffer_size;
char buf[size + 1];
char* end;

float const f = 1;
end = zmij::write(buf, sizeof(buf), f);
*end = '\0';
puts(buf);

std::float16_t const h = 1;
end = zmij::write(buf, sizeof(buf), h);
*end = '\0';
puts(buf);
}
10 changes: 5 additions & 5 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ target_sources(zmij-test PRIVATE pow10-test.cc)

include(CheckCXXCompilerFlag)
check_cxx_compiler_flag(-msse4.1 ZMIJ_HAS_SSE4_1)
if (ZMIJ_HAS_SSE4_1)
add_zmij_test(zmij-sse4-test)
target_compile_options(zmij-sse4-test PRIVATE -msse4.1)
target_compile_definitions(zmij-sse4-test PRIVATE ZMIJ_USE_SSE4_1=1)
endif ()
# if (ZMIJ_HAS_SSE4_1)
# add_zmij_test(zmij-sse4-test)
# target_compile_options(zmij-sse4-test PRIVATE -msse4.1)
# target_compile_definitions(zmij-sse4-test PRIVATE ZMIJ_USE_SSE4_1=1)
# endif ()

add_zmij_test(zmij-c-test)
target_compile_definitions(zmij-c-test PRIVATE ZMIJ_C=1)
Expand Down
31 changes: 28 additions & 3 deletions zmij.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ struct dec_fp {
#include <limits> // std::numeric_limits
#include <type_traits> // std::conditional_t

#ifdef __STDCPP_FLOAT16_T__
#include <stdfloat>
#endif

#ifndef ZMIJ_USE_SIMD
# define ZMIJ_USE_SIMD 1
#endif
Expand Down Expand Up @@ -317,6 +321,13 @@ auto umulhi_inexact_to_odd(uint64_t x_hi, uint64_t, uint32_t y) noexcept
uint64_t p = uint64_t(umul128(x_hi, y) >> 32);
return uint32_t(p >> 32) | ((uint32_t(p) >> 1) != 0);
}
#ifdef __STDCPP_FLOAT16_T__
auto umulhi_inexact_to_odd(uint64_t x_hi, uint64_t, uint16_t y) noexcept
-> uint16_t {
uint64_t p = uint64_t(umul128(x_hi, y) >> 16);
return uint16_t(p >> 16) | ((uint16_t(p) >> 1) != 0);
}
#endif

// Computes the decimal exponent as floor(log10(2**bin_exp)) if regular or
// floor(log10(3/4 * 2**bin_exp)) otherwise, without branching.
Expand All @@ -333,7 +344,8 @@ constexpr auto compute_dec_exp(int bin_exp, bool regular = true) noexcept
template <typename Float> struct float_traits : std::numeric_limits<Float> {
static_assert(float_traits::is_iec559, "IEEE 754 required");

static constexpr int num_bits = float_traits::digits == 53 ? 64 : 32;
static constexpr int num_bits = float_traits::digits == 53 ? 64 :
(float_traits::digits == 24 ? 32 : 16);
static constexpr int num_sig_bits = float_traits::digits - 1;
static constexpr int num_exp_bits = num_bits - num_sig_bits - 1;
static constexpr int exp_mask = (1 << num_exp_bits) - 1;
Expand All @@ -342,6 +354,8 @@ template <typename Float> struct float_traits : std::numeric_limits<Float> {
static constexpr int min_fixed_dec_exp = -4;
static constexpr int max_fixed_dec_exp =
compute_dec_exp(float_traits::digits + 1) - 1;
static constexpr long int threshold = num_bits == 64 ? 1000000000000000 :
(num_bits == 32 ? 10000000 : 10000);

using sig_type = std::conditional_t<num_bits == 64, uint64_t, uint32_t>;
static constexpr sig_type implicit_bit = sig_type(1) << num_sig_bits;
Expand Down Expand Up @@ -908,6 +922,15 @@ ZMIJ_INLINE auto to_digits<32>(uint64_t value, bool, const constants&) noexcept
return {result.bcd + zeros, result.len};
}

#ifdef __STDCPP_FLOAT16_T__
template <>
ZMIJ_INLINE auto to_digits<16>(uint64_t value, bool, const constants&) noexcept
-> dec_digits<16> {
auto result = to_bcd8(value);
return {result.bcd + zeros, result.len};
}
#endif

#if ZMIJ_USE_SIMD_SHUFFLE
struct shuffle_table {
constexpr static bool merge_tables = ZMIJ_USE_SSE4_1;
Expand Down Expand Up @@ -1166,7 +1189,7 @@ auto write(Float value, char* buffer) noexcept -> char* {

const auto* c = &consts;
ZMIJ_ASM(("" : "+r"(c))); // Load constants from memory.
uint64_t threshold = traits::num_bits == 64 ? c->threshold : uint64_t(1e7);
uint64_t threshold = traits::threshold;

to_decimal_result dec;
bool is_normal = unsigned(bin_exp - 1) < unsigned(traits::exp_mask - 1);
Expand Down Expand Up @@ -1256,6 +1279,8 @@ auto write(Float value, char* buffer) noexcept -> char* {

template auto write(float value, char* buffer) noexcept -> char*;
template auto write(double value, char* buffer) noexcept -> char*;

#ifdef __STDCPP_FLOAT16_T__
template auto write(std::float16_t value, char* buffer) noexcept -> char*;
#endif
} // namespace detail
} // namespace zmij
15 changes: 15 additions & 0 deletions zmij.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
#include <stddef.h> // size_t
#include <string.h> // memcpy

#ifdef __STDCPP_FLOAT16_T__
#include <stdfloat>
#endif

namespace zmij {
namespace detail {
template <typename Float>
Expand All @@ -33,6 +37,7 @@ struct dec_fp {
auto to_decimal(double value) noexcept -> dec_fp;

enum {
float16_t_buffer_size = 9,
float_buffer_size = 17,
double_buffer_size = 34,
};
Expand Down Expand Up @@ -63,6 +68,16 @@ inline auto write(char* out, size_t n, double value) noexcept -> char* {
return out + size;
}

#ifdef __STDCPP_FLOAT16_T__
inline auto write(char* out, size_t n, std::float16_t value) noexcept -> char* {
if (n >= float16_t_buffer_size) return detail::write(value, out);
char buffer[float16_t_buffer_size];
size_t size = detail::write(value, buffer) - buffer;
memcpy(out, buffer, n);
return out + size;
}
#endif

} // namespace zmij

#endif // ZMIJ_H_
Loading