C++ Logo

sg12

Advanced search

Re: [ub] Signed shifting

From: Howard Hinnant <howard.hinnant_at_[hidden]>
Date: Mon, 17 Nov 2014 18:14:16 -0500
Examples of real-world code that want to do arithmetic right shifts on both positive and negative values. This code is portable across all two’s complement machines, assuming that the compiler does not take advantage of the current ub. This is the type of code that is in use today, and we are in danger of silently rewriting, either because of allowed compiler optimizations which detect ub and optimize based on the assumption that ub can not happen, or because we might change the definition of arithmetic right shift to be in terms of division by 2^n:

#include <limits>
#include <iostream>

// | -1, x < 0
// sign(x) = | 0, x == 0
// | 1, x > 0
int
sign(int x)
{
    constexpr int n = std::numeric_limits<int>::digits;
    return (x >> n) | (static_cast<unsigned>(-x) >> n);
}

// transfer_sign(x, y) = | abs(x), y >= 0
// | -abs(x), y < 0
int
transfer_sign(int x, int y)
{
    constexpr int n = std::numeric_limits<int>::digits;
    auto const t = (x^y) >> n;
    return (x ^ t) - t;
}

// nabs(int x) = | x > 0, -x
// | x <= 0, x
int
nabs(int x)
{
    constexpr int n = std::numeric_limits<int>::digits;
    auto const t = x >> n;
    return (t - x) ^ t;
}

int
main()
{
    std::cout << "sign(-3) = " << sign(-3) << '\n';
    std::cout << "sign( 0) = " << sign( 0) << '\n';
    std::cout << "sign( 3) = " << sign( 3) << '\n';
    std::cout << '\n';
    std::cout << "transfer_sign(-3, -4) = " << transfer_sign(-3, -4) << '\n';
    std::cout << "transfer_sign( 3, -4) = " << transfer_sign( 3, -4) << '\n';
    std::cout << "transfer_sign(-3, 4) = " << transfer_sign(-3, 4) << '\n';
    std::cout << "transfer_sign( 3, 4) = " << transfer_sign( 3, 4) << '\n';
    std::cout << '\n';
    std::cout << "nabs(-3) = " << nabs(-3) << '\n';
    std::cout << "nabs( 0) = " << nabs(0) << '\n';
    std::cout << "nabs( 3) = " << nabs(3) << '\n';
    std::cout << '\n';
}

sign(-3) = -1
sign( 0) = 0
sign( 3) = 1

transfer_sign(-3, -4) = -3
transfer_sign( 3, -4) = -3
transfer_sign(-3, 4) = 3
transfer_sign( 3, 4) = 3

nabs(-3) = -3
nabs( 0) = 0
nabs( 3) = -3

Howard

Received on 2014-11-18 00:14:21