Use the FLOAT2 data type to compress vector storage
Vector workloads store high-dimensional arrays for every image, audio file, or text embedding processed. At scale, those arrays consume substantial disk space. AnalyticDB for PostgreSQL provides the FLOAT2 data type to store vector columns at half the space of FLOAT4, with minimal impact on search accuracy.
Normalize vectors to the range [0, 1] before running vector search. Values outside the FLOAT2 range (-65519.99 to 65519.99) are stored asInfinityor-Infinity, which produces inaccurate distances.
How FLOAT2 works
FLOAT2 implements the IEEE 754 binary16 (half-precision) format. Each value occupies 2 bytes instead of the 4 bytes used by FLOAT4, giving a storage compression ratio of 0.5 for vector columns.
Format layout
| Field | Width |
|---|---|
| Sign bit | 1 bit |
| Exponent | 5 bits |
| Significand | 11 bits (10 explicitly stored) |
The 10-bit significand provides approximately 3.311 decimal digits of precision (log10(2¹¹) ≈ 3.311).

Representative values
0 01111 0000000000 = 1
0 01111 0000000001 = 1 + 2⁻¹⁰ = 1.0009765625 (smallest number greater than 1)
1 10000 0000000000 = -2
0 11110 1111111111 = 65504 (max half precision)
0 00001 0000000000 = 2⁻¹⁴ ≈ 6.10352 × 10⁻⁵ (smallest positive normal)
0 00000 1111111111 = 2⁻¹⁴ - 2⁻²⁴ ≈ 6.09756 × 10⁻⁵ (greatest subnormal)
0 00000 0000000001 = 2⁻²⁴ ≈ 5.96046 × 10⁻⁸ (smallest positive subnormal)
0 00000 0000000000 = 0
1 00000 0000000000 = -0
0 11111 0000000000 = infinity
1 11111 0000000000 = -infinity
0 01101 0101010101 = 0.333251953125 ≈ 1/31/3 rounds down due to the odd number of bits in the significand — the same behavior as double precision.
FLOAT2 to FLOAT4 conversion
The system converts between FLOAT2 and FLOAT4 by shifting bits and adjusting the exponent base (15 for FLOAT2, 127 for FLOAT4, a difference of 112).
To convert from FLOAT2 to FLOAT4:
Shift the sign bit left by 16 bits.
Add 112 to the exponent base (15 to 127), then shift the exponent left by 13 bits.
Shift the significand left by 13 bits.
The inverse operations apply when converting from FLOAT4 to FLOAT2.
Two conversion algorithms are available:
| Algorithm | Throughput |
|---|---|
| C program (software) | 1 element at a time |
| AVX + SSE2 (hardware) | 4 elements at a time |
Hardware-accelerated conversion requires Advanced Vector Extensions (AVX) and Streaming SIMD Extensions 2 (SSE2) on the host machine. Because vector search uses index traversal rather than full scans, only a small number of entries are converted per query.
Precision and value range
Precision loss: FLOAT2 storage introduces a small amount of precision loss acceptable for vector search. For example, 1.3 is retrieved as 1.2998, and 5.6 is retrieved as 5.60156.
Value range: FLOAT2 accepts values from -65519.99 to 65519.99. Values above 65519 are stored as Infinity; values below -65519 are stored as -Infinity.
Normalization requirement: Normalize vectors to [0, 1] before vector search. Unnormalized vectors can produce distances that exceed the FLOAT2 value range, leading to inaccurate results.
Storage savings
Each FLOAT2 vector element occupies 2 bytes, compared to 4 bytes for FLOAT4. A table with 10,000 rows of 1,024-dimensional vectors illustrates the difference:
-- Create comparison tables
CREATE TABLE TAB1(A FLOAT4[]);
CREATE TABLE TAB2(A FLOAT2[]);
-- Insert 10,000 rows of 1,024-dimensional vectors
INSERT INTO TAB1 SELECT GEN_RAND_F2_ARR(1, 1024) FROM GENERATE_SERIES(1, 10000);
INSERT INTO TAB2 SELECT GEN_RAND_F2_ARR(1, 1024) FROM GENERATE_SERIES(1, 10000);
-- Check sizes
SELECT PG_SIZE_PRETTY(PG_RELATION_SIZE('tab1'));
-- Result: 45 MB
SELECT PG_SIZE_PRETTY(PG_RELATION_SIZE('tab2'));
-- Result: 21 MBThe FLOAT2 table uses 21 MB — roughly half the 45 MB used by the FLOAT4 table.
Create a table with FLOAT2 vectors
FLOAT2 is an internal data type. The system handles type conversions and operators automatically.
CREATE TABLE [TABLE_NAME] (
C1 INT,
C2 FLOAT2[],
C3 VARCHAR(20),
PRIMARY KEY(C1)
) DISTRIBUTED BY(C1);The C2 column stores FLOAT2 vectors.
Example: Create a table named FACE_TABLE with a FLOAT2 vector column.
CREATE TABLE FACE_TABLE (
C1 INT PRIMARY KEY,
C2 FLOAT2[],
C3 VARCHAR(20)
) DISTRIBUTED BY (C1);Insert data
Insert FLOAT2 arrays using any of the following methods. The system automatically converts REAL and FLOAT4 arrays to FLOAT2.
-- Explicit FLOAT2 array
INSERT INTO FACE_TABLE (C1, C2, C3)
VALUES (1, ARRAY[1.3, 2.4, 5.6]::FLOAT2[], 'name1');
-- REAL array (auto-converted to FLOAT2)
INSERT INTO FACE_TABLE (c1, c2, c3)
VALUES (2, ARRAY[3.4, 6.1, 7.6]::REAL[], 'name2');
-- FLOAT4 array (auto-converted to FLOAT2)
INSERT INTO FACE_TABLE (c1, c2, c3)
VALUES (3, ARRAY[9.5, 1.2, 0.6]::FLOAT4[], 'name3');Query data
SELECT * FROM FACE_TABLE;Expected output:
c1 | c2 | c3
----+-------------------------+-------
1 | {1.2998,2.40039,5.60156} | name1
2 | {3.40039,6.10156,7.60156} | name2
3 | {9.5,1.2002,0.600098} | name3Conversion functions
Use array_f16_to_f32 and array_f32_to_f16 to convert between FLOAT2 and FLOAT4 arrays.
| Function | Description |
|---|---|
array_f16_to_f32(arr) | Converts a FLOAT2 array to FLOAT4 |
array_f32_to_f16(arr) | Converts a FLOAT4 array to FLOAT2 |
Performance benchmark — 10,000 rows x 1,024 dimensions on a machine with AVX and SSE2:
\TIMING
-- FLOAT4 to FLOAT2
SELECT ARRAY_F32_TO_F16(a) FROM TAB1;
-- Time: 5998.832 ms (approx. 6 s)
-- FLOAT2 to FLOAT4
SELECT ARRAY_F16_TO_F32(a) FROM TAB2;
-- Time: 5507.388 ms (approx. 5.5 s)Distance calculation
Use L2_DISTANCE to calculate Euclidean distance between FLOAT2 vectors. Mixed FLOAT2 and FLOAT4 operands are supported.
-- FLOAT2 vs FLOAT2
SELECT L2_DISTANCE(
ARRAY[1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0]::FLOAT2[],
ARRAY[0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0]::FLOAT2[]
);
-- FLOAT4 vs FLOAT2 (mixed)
SELECT L2_DISTANCE(
ARRAY[1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0]::FLOAT4[],
ARRAY[0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0]::FLOAT2[]
);Use case: facial recognition
Security systems store captured facial data daily and search for matches based on facial images. The following steps show how to use FLOAT2 for this workflow.
Create a table to store facial data.
CREATE TABLE FACE_TABLE ( C1 INT PRIMARY KEY, -- Face ID C2 FLOAT2[], -- Face vector C3 VARCHAR(20) -- Face name ) DISTRIBUTED BY(C1);Create an Approximate Nearest Neighbor (ANN) index on the vector column.
CREATE INDEX FACE_TABLE_IDX ON FACE_TABLE USING ANN(C2) WITH(dim=10);Insert facial data.
INSERT INTO FACE_TABLE (C1, C2, C3) VALUES (1, ARRAY[1.3, 2.4, 5.6]::FLOAT2[], 'name1'); INSERT INTO FACE_TABLE (c1, c2, c3) VALUES (2, ARRAY[3.4, 6.1, 7.6]::REAL[], 'name2'); INSERT INTO FACE_TABLE (c1, c2, c3) VALUES (3, ARRAY[9.5, 1.2, 0.6]::FLOAT4[], 'name3');Run a vector search using the
<->distance operator.SELECT * FROM FACE_TABLE ORDER BY C1 <-> ARRAY[2.81574,9.84361,8.07218]::FLOAT2[] LIMIT 10;ARRAY[2.81574,9.84361,8.07218]::FLOAT2[]is the query face vector. The database returns the closest matches from the table.