简体   繁体   中英

Why does a cast from floating point to integer truncate?

In C and C++, we all know that converting a floating point value into an integer performs a truncation . That means, a fix towards zero , both for static_cast and for C style casts.

static_assert(static_cast<int>(2.9) == 2);
static_assert((int)-3.7 == -3);

To convert a floating point number into an integer, there are 4 options, each of them is implemented by a standard function from <cmath> :

  1. round
  2. floor
  3. ceil
  4. trunc

I ordered the options from the most often used to the least often used, from my own experience (it is somewhat subjective).

Truncation toward zero means that all values from -0.999... to 0.999... are converted to 0. There is a for that reason a mathematical anomaly near 0, which makes truncation rarely useful. My impression is that nearly every time a programmer directly casts a floating point to an integer, he actually wants floor , but can afford trunc behavior because the value is supposed to be always positive.

My question is: what were the motivations to select the 4th option, truncate? I bet this is mostly historical, but when C was originally developed, there must have been some good reason to select truncation over the more useful rounding or flooring.

You use it when you want to floor the modulus of the value irrespective of the sign. This can happen for multiple reasons that are particular to every application. You might have it in Physics, Finance, Engineering, etc.

On x86_64, trunc(float) is mapped to the roundss instruction on glibc

ENTRY(__truncf_sse41)
        roundss        $11, %xmm0, %xmm0
        ret
END(__truncf_sse41)

The constant $11 above, it's an immediate operand to the actual instruction and determines the rounding characteristics. That particular instruction contain the choices you mentioned (see Intel Intrinsics Guide ):

__m128 _mm_round_ss (__m128 a, __m128 b, int rounding)
Synopsis
__m128 _mm_round_ss (__m128 a, __m128 b, int rounding)
#include <smmintrin.h>
Instruction: roundss xmm, xmm, imm8
CPUID Flags: SSE4.1
Description
Round the lower single-precision (32-bit) floating-point element in b using the rounding parameter, store the result as a single-precision floating-point element in the lower element of dst, and copy the upper 3 packed elements from a to the upper elements of dst.
Rounding is done according to the rounding[3:0] parameter, which can be one of:
    (_MM_FROUND_TO_NEAREST_INT |_MM_FROUND_NO_EXC) // round to nearest, and suppress exceptions
    (_MM_FROUND_TO_NEG_INF |_MM_FROUND_NO_EXC)     // round down, and suppress exceptions
    (_MM_FROUND_TO_POS_INF |_MM_FROUND_NO_EXC)     // round up, and suppress exceptions
    (_MM_FROUND_TO_ZERO |_MM_FROUND_NO_EXC)        // truncate, and suppress exceptions
    _MM_FROUND_CUR_DIRECTION // use MXCSR.RC; see _MM_SET_ROUNDING_MODE

So this is by no means a language choice, it's a demand from users.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM