C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Adding safe saturated floating-point to integer cast to C++26

From: John Platts <john_platts_at_[hidden]>
Date: Tue, 8 Apr 2025 17:01:56 +0000
Here is a snippet on Compiler Explorer that demonstrates suboptimal code generation for saturated floating-point to integer cast (implemented with pure C++11 code) with GCC, Clang, and MSVC: https://godbolt.org/z/rrKGTchGv

std::saturated_cast should be extended to allow for saturated floating point to integer conversions.

std::saturated_cast<int>(std::numeric_limits<float>::quiet_NaN()) should be allowed to return an arbitrary, implementation-defined value that is within the range of an int (and that otherwise has well-defined behavior if no trapping floating point exceptions are raised) to avoid unnecessary overhead on ISA's such as POWER ISA where saturated floating-point to integer conversion instructions return a non-zero result for a NaN input value. In other words, std::saturated_cast<int>(std::numeric_limits<float>::quiet_NaN()) should have the same semantics as __builtin_nondeterministic_value(0), except that std::saturated_cast<int>(std::numeric_limits<float>::quiet_NaN()) is permitted to raise floating-point exceptions.

Here is more optimal assembly language codegen for various saturated floating-point to integer casts on POWER8:
/* std::int32_t SafeSatCastF32ToI32(float val); */
SafeSatCastF32ToI32:
        fctiwz 1,1
        mfvsrwz 3,1
        extsw 3,3
        blr
/* std::uint32_t SafeSatCastF32ToU32(float val); */
SafeSatCastF32ToU32:
        fctiwuz 1,1
        mfvsrwz 3,1
        blr
/* std::int32_t SafeSatCastF64ToI32(double val); */
SafeSatCastF64ToI32:
        fctiwz 1,1
        mfvsrwz 3,1
        extsw 3,3
        blr
/* std::uint32_t SafeSatCastF64ToU32(double val); */
SafeSatCastF64ToU32(double):
        fctiwuz 1,1
        mfvsrwz 3,1
        blr
/* std::int64_t SafeSatCastF64ToI64(double val); */
SafeSatCastF64ToI64(double):
        fctidz 0,1
        mfvsrd 3,0
        blr
/* std::uint64_t SafeSatCastF64ToU64(double val); */
SafeSatCastF64ToU64(double):
        fctiduz 0,1
        mfvsrd 3,0
        blr

Here is more optimal assembly language codegen for various saturated floating-point to integer casts on AArch64:
/* std::int32_t SafeSatCastF32ToI32(float val); */
SafeSatCastF32ToI32:
        fcvtzs w0, s0
        ret
/* std::uint32_t SafeSatCastF32ToU32(float val); */
SafeSatCastF32ToU32:
        fcvtzu w0, s0
        ret
/* std::int32_t SafeSatCastF64ToI32(double val); */
SafeSatCastF64ToI32:
        fcvtzs w0, d0
        ret
/* std::uint32_t SafeSatCastF64ToU32(double val); */
SafeSatCastF64ToU32(double):
        fcvtzu w0, d0
        ret
/* std::int64_t SafeSatCastF64ToI64(double val); */
SafeSatCastF64ToI64(double):
        fcvtzs x0, d0
        ret
/* std::uint64_t SafeSatCastF64ToU64(double val); */
SafeSatCastF64ToU64(double):
        fcvtzu x0, d0
        ret

Here is more optimal assembly language codegen for various saturated floating-point to integer casts on x86 with AVX10.2:
/* std::int32_t SafeSatCastF32ToI32(float val); */
SafeSatCastF32ToI32:
        vcvttss2sis eax, xmm0
        ret
/* std::uint32_t SafeSatCastF32ToU32(float val); */
SafeSatCastF32ToU32:
        vcvttss2usis eax, xmm0
        ret
/* std::int32_t SafeSatCastF64ToI32(double val); */
SafeSatCastF64ToI32:
        vcvttsd2sis eax, xmm0
        ret
/* std::uint32_t SafeSatCastF64ToU32(double val); */
SafeSatCastF64ToU32(double):
        vcvttsd2usis eax, xmm0
        ret
/* std::int64_t SafeSatCastF64ToI64(double val); */
SafeSatCastF64ToI64(double):
        vcvttsd2sis rax, xmm0
        ret
/* std::uint64_t SafeSatCastF64ToU64(double val); */
SafeSatCastF64ToU64(double):
        vcvttsd2usis rax, xmm0
        ret

On POWER8 and AArch64, std::saturated_cast<int>(f64_val) (where f64_val is a double-precision floating point value) should generate the same instruction sequence as static_cast<int>(f64_val), except that the compiler must not assume that the argument of the std::saturated_cast<int>(f64_val) is within the range of an int and that std::saturated_cast<int>(f64_val) has well-defined behavior if no floating point exceptions are raised, even if f64_val is outside of the range of an int.

Implementing optimal std::saturated_cast for float to int conversions requires support from the compiler as a generic, portable pure C++ implementation is suboptimal on many ISA's and as static_cast<int> invokes undefined behavior for values that are outside of the range of an int.

________________________________
From: Std-Proposals <std-proposals-bounces_at_[hidden]> on behalf of Thiago Macieira via Std-Proposals <std-proposals_at_[hidden]>
Sent: Tuesday, April 8, 2025 10:17 AM
To: std-proposals_at_[hidden] <std-proposals_at_[hidden]>
Cc: Thiago Macieira <thiago_at_[hidden]>
Subject: Re: [std-proposals] Adding safe saturated floating-point to integer cast to C++26

On Tuesday, 8 April 2025 07:32:10 Pacific Daylight Time Tiago Freire via Std-
Proposals wrote:
> You can write a proposal.
> Although I don't see why this couldn't easily be done as a library.

We have std::saturate_cast for integers. It should be easy to extend it to
take FP inputs.

The proposal should discuss what happens to a NaN input.

--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
  Principal Engineer - Intel DCAI Platform & System Engineering
--
Std-Proposals mailing list
Std-Proposals_at_[hidden]
https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals

Received on 2025-04-08 17:02:02