NSIMD documentation
Index | Tutorial | FAQ | Contribute | API overview | API reference | Wrapped intrinsics | Modules
Fixed-point arithmetic module documentation
Overview | API reference

NSIMD fixed point module

Description

This module implements a fixed-point numbers support for the nsimd library. Fixed-point numbers are integer types used to represent decimal numbers. A number lf of bits are used to encode its integer part, and rt bits are used to encode its fractional part.

The fixed_point module uses the templated type nsimd::fixed_point::fp_t<lf, rt> to represent a fixed_point number. All the basic floating-point arithmetic operaors have been defined, therefore fp_t elements can be manipulated as normal numbers. The fixed_point module will use a i8, i16, or i32 integer type for storage, depending on the value of lf + 2 * rt.

All the functions of the module are under the namespace nsimd::fixed_point, and match the same interface than nsimd C++ .

The fp_t struct type is defined in fixed.hpp, and the associated simd fpsimd_t struct type are defined in simd.hpp.

The modules redefines the nsimd pack type for fixed-point numbers, templated with lf and rt :

namespace nsimd {
namespace fixed_point {
template <u8 lf, u8 rt>
struct pack;
} // namespace fixed_point
} // namespace nsimd

Then, the pack can be manipulated as an nsimd pack like other scalar types.

Compatibility

The fixed point module is a C++ only API, compatible with the C++98 standard. It has the same compilers and hardware support than the main nsimd API (see the API index).

Example

Here is a minimal example(main.cpp):

#include <ctime>
#include <cstdlib>
#include <iostream>
#include <nsimd/modules/fixed_point.hpp>

float rand_float() {
  return 4.0f * ((float) rand() / (float) RAND_MAX) - 2.0f;        
}

int main() {
  // We use fixed point numbers with 8 bits of integer part and 8 bits of 
  // decimal part. It will use 32 bits integers for internal storage.
  typedef nsimd::fixed_point::fp_t<8, 8> fp_t;
  typedef nsimd::fixed_point::pack<fp_t> fp_pack_t;
  
  const size_t v_size = nsimd::fixed_point::len(fp_t());

  fp_t *input0 = (fp_t*)malloc(v_size * sizeof(fp_t));
  fp_t *input1 = (fp_t *)malloc(v_size * sizeof(fp_t));
  fp_t *res = (fp_t *)malloc(v_size * sizeof(fp_t));
  
  // Input and output initializations 
  for(size_t i = 0; i < nsimd::fixed_point::len(fp_t()); i++) {
    input0[i] = fp_t(rand_float());
    input1[i] = fp_t(rand_float());
  }
  
  fp_pack_t v0 = nsimd::fixed_point::loadu<fp_pack_t>(input0);
  fp_pack_t v1 = nsimd::fixed_point::loadu<fp_pack_t>(input1);
  fp_pack_t vres = nsimd::fixed_point::add(v0, v1);
  nsimd::fixed_point::storeu(res, vres);
  
  for(size_t i = 0; i < nsimd::fixed_point::len(fp_t()); i++) {
    std::cout << float(input0[i]) << " | "
      << float(input1[i]) << " | "
      << float(res[i]) << "\n";
  }
  std::cout << std::endl;
  
  return EXIT_SUCCESS;
}

To test with avx2 run :

export NSIMD_ROOT=<path/to/nsimd>
g++ -o main -I$NSIMD_ROOT/include -mavx2 -DNSIMD_AVX2 main.cpp
./main

The console output will look like this :

$>./main
1.35938 | -0.421875 | 0.9375
1.13281 | 1.19531 | 2.32812
1.64844 | -1.21094 | 0.4375
-0.660156 | 1.07422 | 0.414062
-0.890625 | 0.214844 | -0.675781
-0.0898438 | 0.515625 | 0.425781
-0.539062 | 0.0546875 | -0.484375
1.80859 | 1.66406 | 3.47266