Saltar al contenido

Conversión de coma flotante de 32 bits a 16 bits

Te doy la bienvenida a proyecto on line, ahora vas a encontrar la respuesta de lo que estás buscando.

Solución:

Conversión completa de precisión simple a precisión media. Esta es una copia directa de mi versión SSE, por lo que no tiene sucursales. Hace uso del hecho de que -true == ~0 para realizar selecciones sin ramificaciones (GCC convierte if declaraciones en un lío impío de saltos condicionales, mientras que Clang simplemente los convierte en movimientos condicionales).

Actualización (2019-11-04): reelaborado para admitir soltero y valores de doble precisión con redondeo completamente correcto. También pongo un correspondiente if declaración sobre cada selección sin ramas como comentario para mayor claridad. Todos los NaN entrantes se convierten al NaN silencioso básico para mayor velocidad y cordura, ya que no hay forma de convertir de manera confiable un mensaje NaN incrustado entre formatos.

#include  // uint32_t, uint64_t, etc.
#include  // memcpy
#include  // CHAR_BIT
#include   // numeric_limits
#include  // is_integral_v, is_floating_point_v, forward

namespace std

  template< typename T , typename U >
  T bit_cast( U&& u ) 
    static_assert( sizeof( T ) == sizeof( U ) );
    union  T t; ; // prevent construction
    std::memcpy( &t, &u, sizeof( t ) );
    return t;
  
 // namespace std

template< typename T > struct native_float_bits;
template<> struct native_float_bits< float > using type = std::uint32_t; ;
template<> struct native_float_bits< double > using type = std::uint64_t; ;
template< typename T > using native_float_bits_t = typename native_float_bits< T >::type;

static_assert( sizeof( float ) == sizeof( native_float_bits_t< float > ) );
static_assert( sizeof( double ) == sizeof( native_float_bits_t< double > ) );

template< typename T, int SIG_BITS, int EXP_BITS >
struct raw_float_type_info 
  using raw_type = T;

  static constexpr int sig_bits = SIG_BITS;
  static constexpr int exp_bits = EXP_BITS;
  static constexpr int bits = sig_bits + exp_bits + 1;

  static_assert( std::is_integral_v< raw_type > );
  static_assert( sig_bits >= 0 );
  static_assert( exp_bits >= 0 );
  static_assert( bits <= sizeof( raw_type ) * CHAR_BIT );

  static constexpr int exp_max = ( 1 << exp_bits ) - 1;
  static constexpr int exp_bias = exp_max >> 1;

  static constexpr raw_type sign = raw_type( 1 ) << ( bits - 1 );
  static constexpr raw_type inf = raw_type( exp_max ) << sig_bits;
  static constexpr raw_type qnan = inf ;
using raw_flt16_type_info = raw_float_type_info< std::uint16_t, 10, 5 >;
using raw_flt32_type_info = raw_float_type_info< std::uint32_t, 23, 8 >;
using raw_flt64_type_info = raw_float_type_info< std::uint64_t, 52, 11 >;
//using raw_flt128_type_info = raw_float_type_info< uint128_t, 112, 15 >;

template< typename T, int SIG_BITS = std::numeric_limits< T >::digits - 1,
  int EXP_BITS = sizeof( T ) * CHAR_BIT - SIG_BITS - 1 >
struct float_type_info 
: raw_float_type_info< native_float_bits_t< T >, SIG_BITS, EXP_BITS > 
  using flt_type = T;
  static_assert( std::is_floating_point_v< flt_type > );
;

template< typename E >
struct raw_float_encoder

  using enc = E;
  using enc_type = typename enc::raw_type;

  template< bool DO_ROUNDING, typename F >
  static auto encode( F value )
  = sign >> bit_diff; // restore sign
    return enc_type( bits );
  

  template< typename F >
  static F decode( enc_type value )
  = -!is_norm & flt::inf;
    return std::bit_cast< F >( bits );
  
;

using flt16_encoder = raw_float_encoder< raw_flt16_type_info >;

template< typename F >
auto quick_encode_flt16( F && value )
 return flt16_encoder::encode< false >( std::forward< F >( value ) ); 

template< typename F >
auto encode_flt16( F && value )
 return flt16_encoder::encode< true >( std::forward< F >( value ) ); 

template< typename F = float, typename X >
auto decode_flt16( X && value )
 return flt16_encoder::decode< F >( std::forward< X >( value ) ); 

Por supuesto, no siempre se necesita el soporte completo de IEEE. Si sus valores no requieren una resolución logarítmica cercana a cero, linealizarlos a un formato de punto fijo es mucho más rápido, como ya se mencionó.

Mitad para flotar:
float f = ((h&0x8000)<<16) | (((h&0x7c00)+0x1C000)<<13) | ((h&0x03FF)<<13);

Flotar a la mitad:
uint32_t x = *((uint32_t*)&f);
uint16_t h = ((x>>16)&0x8000)|((((x&0x7f800000)-0x38000000)>>13)&0x7c00)|((x>>13)&0x03ff);

std::frexp extrae el significado y el exponente de flotantes normales o dobles; luego debe decidir qué hacer con exponentes que son demasiado grandes para caber en un flotador de precisión media (¿saturar ...?), ajustar en consecuencia y poner la mitad. número de precisión juntos. Este artículo tiene código fuente C para mostrarle cómo realizar la conversión.

Te mostramos reseñas y valoraciones

Tienes la opción de añadir valor a nuestro contenido informacional tributando tu experiencia en las explicaciones.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *