Compartir a través de


Conversión de tipos de datos

En las secciones siguientes se describe cómo Direct3D controla las conversiones entre tipos de datos.

Terminología del tipo de datos

El siguiente conjunto de términos se usa en adelante para caracterizar varias conversiones de formato.

Término Definición
SNORM Entero normalizado con signo, lo que significa que para un número de complemento de n bits 2, el valor máximo significa 1,0f (por ejemplo, el valor de 5 bits 01111 se asigna a 1,0f) y el valor mínimo significa -1,0f (por ejemplo, el valor de 5 bits 10000 se asigna a -1,0f). Además, el segundo número mínimo se asigna a -1,0f (por ejemplo, el valor de 5 bits 10001 se asigna a -1,0f). Por lo tanto, hay dos representaciones enteras para -1,0f. Hay una sola representación para 0,0f y una sola representación para 1,0f. Esto da como resultado un conjunto de representaciones de enteros para valores de punto flotante espaciados uniformemente en el intervalo (-1,0f... 0,0f) y también un conjunto complementario de representaciones para números en el intervalo (0,0f... 1,0f)
UNORM Entero normalizado sin signo, lo que significa que para un número de n bits, todos los 0 significan 0,0f y todos los 1 significan 1,0f. Se representa una secuencia de valores de punto flotante espaciados uniformemente de 0,0f a 1,0f. por ejemplo, un UNORM de 2 bits representa 0,0f, 1/3, 2/3 y 1,0f.
SINT Entero con signo. Enteros complementarios del 2. por ejemplo, un SINT de 3 bits representa los valores enteros -4, -3, -2, -1, 0, 1, 2, 3.
UINT Entero sin signo. por ejemplo, un UINT de 3 bits representa los valores enteros 0, 1, 2, 3, 4, 5, 6, 7.
FLOAT Valor de punto flotante en cualquiera de las representaciones definidas por Direct3D.
SRGB De forma similar a UNORM, en que para un número de n bits, todos los 0 significan 0,0f y todos los 1 significan 1,0f. Sin embargo, a diferencia de UNORM, con SRGB la secuencia de codificaciones de enteros sin signo entre los 0 y los 1 representa una progresión no lineal en la interpretación del punto flotante de los números, entre 0,0f y 1,0f. Aproximadamente, si esta progresión no lineal, SRGB, se muestra como una secuencia de colores, aparecería como una rampa lineal de niveles de luminosidad a un observador "promedio", en condiciones de visualización "promedio", en una pantalla "promedio". Para obtener detalles completos, consulta el estándar de color SRGB, CEI 61996-2-1, en CEI (Comisión electrotécnica internacional).

 

Los términos anteriores se usan a menudo como "Modificadores de nombre de formato", donde describen cómo se distribuyen los datos en la memoria y qué conversión realizar en la ruta de acceso de transporte (posiblemente incluido el filtrado) de la memoria a o desde una unidad de canalización, como un sombreador.

Conversión de punto flotante

Cada vez que se produce una conversión de punto flotante entre diferentes representaciones, incluidas las representaciones de punto no flotante o desde ellas, se aplican las siguientes reglas.

Conversión de una representación de intervalo superior a una representación de intervalo inferior

  • Se usa redondeo a cero durante la conversión a otro formato de punto flotante. Si el destino es un formato entero o de punto fijo, se usa el tipo redondear al par más cercano, a menos que la conversión se documente explícitamente como el uso de otro comportamiento de redondeo, como redondeo al más cercano de FLOAT a SNORM, FLOAT a UNORM o FLOAT a SRGB. Otras excepciones son las instrucciones de sombreador ftoi y ftou, que usan redondeo a cero. Por último, las conversiones de flotante a fijo usadas por el muestreador de textura y el rasterizador tienen una tolerancia especificada medida en unidad del último lugar desde un ideal infinitamente preciso.
  • En el caso de los valores de origen mayores que el intervalo dinámico de un formato de destino de intervalo inferior (por ejemplo, un valor flotante de 32 bits grande se escribe en un RenderTarget flotante de 16 bits), el valor máximo que se puede representar (con el signo adecuado), SIN incluir el infinito con signo (debido al redondeo a cero descrito anteriormente).
  • NaN en un formato de rango superior se convertirá en manifestación NaN en el formato de rango inferior si la representación NaN existe en el formato de rango inferior. Si el formato inferior no tiene una manifestación NaN, el resultado será 0.
  • INF en un formato de rango superior se convertirá a INF en el formato de rango inferior si está disponible. Si el formato inferior no tiene una representación INF, se convertirá al valor máximo que se puede representar. El signo se conservará si está disponible en el formato de destino.
  • Denorm en un formato de rango superior se convertirá en la manifestación Denorm en el formato de rango inferior si está disponible en el formato de rango inferior y la conversión es posible; de lo contrario, el resultado es 0. El bit de signo se conservará si está disponible en el formato de destino.

Convertir desde una representación de rango inferior a una representación de rango superior

  • NaN en un formato de rango inferior se convertirá en manifestación NaN en el formato de rango superior si está disponible en el formato de rango superior. Si el formato de intervalo superior no tiene una manifestación NaN, se convertirá a 0.
  • INF en un formato de rango inferior se convertirá en manifestación INF en el formato de rango superior si está disponible en el formato de rango superior. Si el formato superior no tiene una representación INF, se convertirá al valor máximo que se puede representar (MAX_FLOAT en ese formato). El signo se conservará si está disponible en el formato de destino.
  • Denorm en un formato de rango inferior se convertirá en una manifestación normalizada en el formato de rango superior, si es posible, o en una manifestación de Denorm en el formato de rango superior si existe la manifestación de Denorm. Si estos fallan, si el formato de intervalo superior no tiene una manifestación Denorm, se convertirá a 0. El signo se conservará si está disponible en el formato de destino. Ten en cuenta que los números de punto flotante de 32 bits cuentan como un formato sin una manifestación de Denorm (ya que los Denorms en operaciones en flotantes de 32 bits vacían a 0 con el signo conservado).

Conversión de enteros

En la tabla siguiente se describen las conversiones de varias representaciones descritas anteriormente a otras representaciones. Solo se muestran las conversiones que se producen realmente en Direct3D.

Con enteros, a menos que se especifique lo contrario, se realizarán exactamente todas las conversiones de y a manifestaciones de enteros a las manifestaciones de flotantes descritas a continuación.

Tipo de datos de origen Tipo de datos de destino Regla de conversión
SNORM FLOAT

Dado un valor entero de n bits que representa el intervalo firmado [-1,0f a 1,0f], la conversión a punto flotante es la siguiente.

  • El valor más negativo se asigna a -1,0f. por ejemplo, el valor de 5 bits 10000 se asigna a -1,0f.
  • El resto de valores se convierten en un número de punto flotante (llámalo c) y, a continuación, result = c * (1,0f / (2⁽ⁿ⁻¹⁾-1)). Por ejemplo, el valor de 5 bits 10001 se convierte en -15,0f y, a continuación, se divide por 15,0f, lo que produce -1,0f.
FLOAT SNORM

Dado un número de punto flotante, la conversión a un valor entero de n bits que representa el intervalo con signo [-1,0f a 1,0f] es el siguiente.

  • Pongamos que c representa el valor inicial.
  • Si c es NaN, el resultado es 0.
  • Si c > 1,0f, incluido INF, se sujeta a 1,0f.
  • Si c < -1,0f, incluido -INF, se sujeta a -1,0f.
  • Convertir de escala flotante a escala de enteros: c = c * (2ⁿ⁻¹-1).
  • Convertir a un entero como se indica a continuación.
    • Si c >= 0, entonces c = c + 0,5f; en caso contrario, c = c - 0,5f.
    • Elimina la fracción decimal y el valor de punto flotante restante (entero) se convierte directamente en un entero.

Esta conversión permite una tolerancia de D3Dxx_FLOAT32_TO_INTEGER_TOLERANCE_IN_Unit-Last-Place Unit-Last-Place (en el lado entero). Esto significa que después de convertir de escala flotante a escala de enteros, cualquier valor de D3Dxx_FLOAT32_TO_INTEGER_TOLERANCE_IN_ULP Unit-Last-Place de un valor de formato de destino que se pueda representar se puede asignar a ese valor. El requisito adicional de invertibilidad de datos garantiza que la conversión no deje de crearse en el intervalo y se puedan alcanzar todos los valores de salida. (En las constantes que se muestran aquí, xx debe reemplazarse por la versión de Direct3D, por ejemplo 10, 11 o 12).

UNORM FLOAT

El valor inicial de n bits se convierte en float (0,0f, 1,0f, 2,0f, etc.) y, a continuación, se divide por (2ⁿ-1).

FLOAT UNORM

Pongamos que c representa el valor inicial.

  • Si c es NaN, el resultado es 0.
  • Si c > 1,0f, incluido INF, se sujeta a 1,0f.
  • Si c < 0,0f, incluido -INF, se sujeta a 0,0f.
  • Convertir de escala flotante a escala de enteros: c = c * (2ⁿ-1).
  • Convertir en entero.
    • c = c + 0,5f.
    • Se elimina la fracción decimal y el valor de punto flotante restante (entero) se convierte directamente en un entero.

A esta conversión se le permite una tolerancia de D3Dxx_FLOAT32_TO_INTEGER_TOLERANCE_IN_ULP Unit-Last-Place (en el lado entero). Esto significa que después de convertir de escala flotante a escala de enteros, cualquier valor de D3Dxx_FLOAT32_TO_INTEGER_TOLERANCE_IN_ULP Unit-Last-Place de un valor de formato de destino que se pueda representar se puede asignar a ese valor. El requisito adicional de invertibilidad de datos garantiza que la conversión no deje de crearse en el intervalo y se puedan alcanzar todos los valores de salida.

SRGB FLOAT

A continuación se muestra la conversión SRGB a FLOAT ideal.

  • Toma el valor inicial de n bits, conviértelo en un valor float (0,0f, 1,0f, 2,0f, etc.) y llámalo c.
  • c = c * (1,0f / (2ⁿ-1))
  • Si (c < = D3Dxx_SRGB_TO_FLOAT_THRESHOLD), entonces: result = c / D3Dxx_SRGB_TO_FLOAT_DENOMINATOR_1, else: result = ((c + D3Dxx_SRGB_TO_FLOAT_OFFSET)/D3Dxx_SRGB_TO_FLOAT_DENOMINATOR_2)D3Dxx_SRGB_TO_FLOAT_EXPONENT

A esta conversión se le permite una tolerancia de D3Dxx_SRGB_TO_FLOAT_TOLERANCE_IN_ULP Unit-Last-Place (en el lado SRGB).

FLOAT SRGB

A continuación se muestra la conversión FLOAT -> SRGB ideal.

Suponiendo que el componente de color SRGB de destino tenga n bits:

  • Supongamos que el valor inicial es c.
  • Si c es NaN, el resultado es 0.
  • Si c > 1,0f, incluido INF, se sujeta a 1,0f.
  • Si c < 0,0f, incluido -INF, se sujeta a 0,0f.
  • Si (c <= D3D xx_FLOAT_TO_SRGB_THRESHOLD) entonces: c = D3Dxx_FLOAT_TO_SRGB_SCALE_1 * c, else: c = D3Dxx_FLOAT_TO_SRGB_SCALE_2 * c(D3Dxx_FLOAT_TO_SRGB_EXPONENT_NUMERATOR/D3Dxx_FLOAT_TO_SRGB_EXPONENT_DENOMINATOR) - D3Dxx_FLOAT_TO_SRGB_OFFSET
  • Convertir de escala flotante a escala de enteros: c = c * (2ⁿ-1).
  • Convertir en entero:
    • c = c + 0,5f.
    • Se elimina la fracción decimal y el valor de punto flotante restante (entero) se convierte directamente en un entero.

A esta conversión se le permite una tolerancia de D3Dxx_FLOAT32_TO_INTEGER_TOLERANCE_IN_ULP Unit-Last-Place (en el lado entero). Esto significa que después de convertir de escala flotante a escala de enteros, cualquier valor de D3Dxx_FLOAT32_TO_INTEGER_TOLERANCE_IN_ULP Unit-Last-Place de un valor de formato de destino que se pueda representar se puede asignar a ese valor. El requisito adicional de invertibilidad de datos garantiza que la conversión no deje de crearse en el intervalo y se puedan alcanzar todos los valores de salida.

SINT SINT con más bits

Para convertir de SINT a SINT con más bits, el bit más significativo (MSB) del número inicial "extiende el signo" a los bits adicionales disponibles en el formato de destino.

UINT SINT con más bits

Para convertir de UINT a un SINT con más bits, el número se copia en los bits menos significativos del formato de destino (LSB) y los MSB adicionales se rellenan con 0.

SINT UINT con más bits

Para convertir de SINT a UINT con más bits: si es negativo, el valor se fija a 0. De lo contrario, el número se copia en los LSB del formato de destino y los MSB adicionales se rellenan con 0.

UINT UINT con más bits

Para convertir de UINT a UINT con más bits, el número se copia a los LSB del formato de destino y los MSB adicionales se rellenan con 0.

SINT o UINT SINT o UINT con menos o los mismos bits

Para convertir de un SINT o UINT a SINT o UINT con menos o los mismos bits (o cambiar el signo), el valor inicial simplemente se fija al rango del formato de destino.

 

Conversión de enteros de punto fijo

Los enteros de punto fijo son simplemente enteros de un tamaño de bits considerable que tienen un separador decimal implícito en una ubicación fija.

El tipo de datos "entero" omnipresente es un caso especial de un entero de punto fijo con el decimal al final del número.

Las manifestaciones de números de punto fijo se caracterizan por: i.f, donde i es el número de bits enteros y f es el número de bits fraccionarios. por ejemplo, 16,8 significa 16 bits enteros seguidos de 8 bits de fracción. La parte entera se almacena en el complemento de 2, al menos como se define aquí (aunque también se puede definir para enteros sin signo). La parte fraccional se almacena en forma sin firmar. La parte fraccional siempre representa la fracción positiva entre los dos valores enteros más cercanos, empezando por el más negativo.

Las operaciones de suma y resta en números de punto fijo se realizan simplemente mediante la aritmética de enteros estándar, sin tener en cuenta dónde se encuentra el decimal implícito. Agregar 1 a un número fijo de 16,8 simplemente significa agregar 256, ya que el decimal se encuentra a 8 lugares del extremo menos significativo del número. Otras operaciones, como la multiplicación, se pueden realizar simplemente mediante la aritmética de enteros, siempre que se tenga en cuenta el efecto sobre el decimal fijo. Por ejemplo, la multiplicación de dos enteros de 16,8 mediante una multiplicación de enteros genera un resultado de 32,16.

Las manifestaciones de enteros de punto fijo se usan de dos maneras en Direct3D.

  • Las posiciones posteriores de vértices recortadas en el rasterizador se acoplan a un punto fijo para distribuir uniformemente la precisión en el área RenderTarget. Muchas operaciones de rasterizador, incluida la selección de caras como ejemplo, se producen en posiciones fijas acopladas de punto, mientras que otras operaciones, como la configuración del interpolador de atributos, usan posiciones que se han convertido de nuevo en punto flotante desde las posiciones acopladas de punto fijo.
  • Las coordenadas de textura para las operaciones de muestreo se acoplan a un punto fijo (después de escalarse por tamaño de textura), para distribuir uniformemente la precisión entre el espacio de textura, al elegir las ubicaciones o pesos de pulsación de filtro. Los valores de peso se convierten de nuevo en punto flotante antes de que se realice el filtrado real.
Tipo de datos de origen Tipo de datos de destino Regla de conversión
FLOAT Entero de punto fijo

A continuación se muestra el procedimiento general para convertir un número de punto flotante n en un entero de punto fijo i.f, donde i es el número de bits enteros (con signo) y f es el número de bits fraccionarios.

  • Computar FixedMin = -2⁽ⁱ⁻¹⁾
  • Computar FixedMax = 2⁽ⁱ⁻¹⁾ - 2(-f)
  • Si n es un NaN, result = 0; si n es +Inf, result = FixedMax*2f; si n es -Inf, result = FixedMin*2f
  • Si n >= FixedMax, result = Fixedmax*2f; if n <= FixedMin, result = FixedMin*2f
  • De lo contrario, calcula n*2f y convierte en entero.

Se permiten implementaciones con tolerancia D3Dxx_FLOAT32_TO_INTEGER_TOLERANCE_IN_ULP Unit-Last-Place en el resultado entero, en lugar del valor infinito n*2f después del último paso anterior.

Entero de punto fijo FLOAT

Supongamos que la manifestación de punto fijo específica que se convierte en float no contiene más de un total de 24 bits de información, no más de 23 bits de los cuales se encuentran en el componente fraccionario. Supongamos que un número de punto fijo determinado, fxp, está en forma i.f (entero de i bits, fracción de f bits). La conversión a float es similar al pseudocódigo siguiente.

float result = (float)(fxp >> f) + // extract integer

((float)(fxp & (2f - 1)) / (2f)); // extract fraction

 

Apéndices