简体   繁体   中英

Using builtin overflow functions with uint64_t types

I am currently writing a program which makes heavy use of the uint64_t type, to enhance compatibility across platforms. As it happens, there are a lot of possibilities for overflows in my program, so I want to use the built in overflow functions from gcc .

Unfortunately, they are only defined on int s, long int s, long long int s and so on. The uintX_t types guarantee to always be of size X bits, which is not the case for those types eg a long int is not guaranteed to be 64 bit. This makes me think, that the builtin overflow functions can't be used here.

How to solve this issue now?

I have two approaches:

  1. using the UINT64_MAX constant from stdint.h and make the overflow prediction myself. However, I am not a friend of "re-inventing the wheel".

  2. using the eg __builtin_add_overflow_p function to only check for the overflow. However, I am not 100% sure if they can be applied to uint64_t .

What is the best way? Am I overseeing something obvious?

Using builtin overflow functions with uint64_t
... to use the built in overflow functions from gcc .

To form your own builtin_uadd64_overflow() , without "re-inventing the wheel", use _Generic to steer function selection.

#define builtin_uadd64_overflow(a,b,r) _Generic(*(r), \
  unsigned: __builtin_uadd_overflow, \
  unsigned long: __builtin_uaddl_overflow, \
  unsigned long long: __builtin_uaddll_overflow \
  )(a,b,r)

Unsigned overflow is defined behaviour according to the C standard (§6.2.5/9)

A computation involving unsigned operands can never overflow, because a result that cannot be represented by the resulting unsigned integer type is reduced modulo the number that is one greater than the largest value that can be represented by the resulting type.

So when you have an addition like;

uint64_t a, b, c;

c = a + b;

you can check, if the operation overflowed by testing

int overflowed = c < a || c < b;

Of course, you can wrap this into a function if you need.

The following functions should work pretty well for objects of type int or long long ; on 32-bit platforms, the int functions will be much more efficient than approaches using long long ; on 64-bit platforms, the long long functions will be as efficient as the int ones.

int safe_add1(int  *value, int  delta) {
    int val = *value;
    int sum = delta+(unsigned int)val;
    if (((sum ^ val) & (sum ^ delta)) < 0)
        return -1;
    *value = sum;
    return 0;
}
int safe_add2(long long  *value, long long  delta) {
    long long val = *value;
    long long sum = delta+(unsigned long long)val;
    if (((sum ^ val) & (sum ^ delta)) < 0)
        return -1;
    *value = sum;
    return 0;
}

Note that to guarding against overflow really efficiently would in many cases require a compiler to use integer overflow as an invitation to implement a conforming extension that would define integer overflow semantics in a way that was tight enough to meet program requirements, but loose enough to allow optimizations. For example, if a program wants to compute x+y > z while ensuring that no overflows that would yield numerically-wrong behavior go undetected, and if a compiler knows that x and z will be equal, the optimal way to meet requirements would be to compute y > 0 , since that behavior would be numerically correct whether or not x+y could be computed without overflow.

Unfortunately, unless compilers provide extensions to recognize such constructs, there will be no way to write particularly efficient overflow-checking code.

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