Megatech


to_human_readable

#include <cmath>

#include <string>
#include <sstream>
#include <iomanip>
#include <limits>
#include <bit>

enum class byte_unit_type {
  binary, // 1 KiB = 1024 B
  decimal, // 1 KB = 1000 B (like hard drive vendors)
  customary // 1 KB = 1024 B (like Windows)
};

template <byte_unit_type Units = byte_unit_type::binary>
std::string to_human_readable(const std::size_t bytes) {
  constexpr auto units = "KMGTPEiB";
  constexpr auto unit_ending = Units == byte_unit_type::binary ? &units[6] : &units[7];
  constexpr auto kilo = Units != byte_unit_type::decimal ? 1024 : 1000;
  auto format_stream = std::ostringstream{ };
  if (bytes < kilo)
  {
    format_stream << bytes << " Bytes";
  }
  else
  {
    format_stream << std::fixed << std::setprecision(2);
    if constexpr (kilo == 1024)
    {
      constexpr auto bits = std::numeric_limits<std::size_t>::digits;
      const auto zeroes = (bits - std::countl_zero(bytes) - 1) / 10;
      const auto divisor = static_cast<double>(std::size_t{ 1 } << (zeroes * 10));
      format_stream << static_cast<double>(bytes) / divisor;
      format_stream << " " << units[zeroes - 1];
    }
    else if constexpr (kilo == 1000)
    {
      const auto pow1000 = static_cast<std::size_t>(std::floor(std::log(bytes) /
                                                    std::log(1000)));
      format_stream << static_cast<double>(bytes) / std::pow(1000, pow1000);
      format_stream << " " << units[pow1000 - 1];
    }
    format_stream << unit_ending;
  }
  return format_stream.str();
}