Can anyone provide some code examples that act differently when compiled with -fwrapv
vs without?
The gcc documentation says that -fwrapv
instructs the compiler to assume that signed arithmetic overflow of addition, subtraction and multiplication wraps around using twos-complement representation.
But whenever I try overflowing the result is the same with or without -fwrapv
.
Definition of osteopathy : a system of medical practice that emphasizes a holistic and comprehensive approach to patient care and utilizes the manipulation of musculoskeletal tissues along with other therapeutic measures (such as the use of drugs or surgery) to prevent and treat disease : osteopathic medicine.
A Hands-On Approach DOs use osteopathic manipulative treatment (OMT) to help identify and correct the source of the underlying health concerns. They use this technique to help treat low back pain, as well as a variety of other health problems, including headaches and sinus issues.
A doctor of osteopathic medicine (D.O.) is a fully trained and licensed doctor who has attended and graduated from a U.S. osteopathic medical school. A doctor of medicine (M.D.) has attended and graduated from a conventional medical school.
Think of this function:
int f(int i) {
return i+1 > i;
}
Mathematically speaking, i+1
should always be greater than i
for any integer i
. However, for a 32-bit int
, there is one value of i
that makes that statement false, which is 2147483647
(i.e. 0x7FFFFFFF
, i.e. INT_MAX
). Adding one to that number will cause an overflow and the new value, according to the 2's compliment representation, will wrap-around and become -2147483648
. Hence, i+1>i
becomes -2147483648>2147483647
which is false.
When you compile without -fwrapv
, the compiler will assume that the overflow is 'non-wrapping' and it will optimize that function to always return 1
(ignoring the overflow case).
When you compile with -fwrapv
, the function will not be optimized, and it will have the logic of adding 1 and comparing the two values, because now the overflow is 'wrapping' (i.e. the overflown number will wrap according to the 2's compliment representation).
The difference can be easily seen in the generated assembly - in the right pane, without -fwrapv
, function always returns 1
(true
).
for (int i=0; i>=0; i++)
printf("%d\n", i);
With -fwrapv
, the loop will terminate after INT_MAX
iterations. Without, it could do anything since undefined behavior is unconditionally invoked by evaluation of i++
when i
has the value INT_MAX
. In practice, an optimizing compiler will likely omit the loop condition and produce an infinite loop.
The ISO standard working group WG14 exists to establish a convention that all C compilers must adhere to. Some compilers may (and do) also implement extensions. According to ISO standard C, those extensions are considered one of the following:
C11/3.4.3 establishes a definition for undefined behaviour and gives an extremely familiar example, which is vastly superior to anything I could write:
1 undefined behavior behavior, upon use of a nonportable or erroneous program construct or of erroneous data, for which this International Standard imposes no requirements2 NOTE Possible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).
3 EXAMPLE An example of undefined behavior is the behavior on integer overflow.
There's also an unspecified behaviour, though I'll leave it as an exercise to you to read about that in the standard.
Be careful where you tread. This is one of the few generally accepted undefined behaviours where it's typically expected that a LIA-style wrapping will occur upon a twos complement representation without a trap repesentation. It's important to realise that there are implementations that use a trap representation corresponding to the bit representation containing all ones.
In summary, fwrapv
and ftrapv
exist to pass on a choice to you, a choice which the developers would have otherwise had to make on your behalf, and that choice is what happens when signed integer overflow occurs. Of course, they must select a default, which in your case appears to correlate to fwrapv
rather than ftrapv
. That needn't be the case, and it needn't be the case that these compiler options change anything what-so-ever.
When using a new version of the gcc compiler I stumbled into a problem that made the flag -fwrapv clear.
char Data[8]={0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
int a=256*(256*(256*Data[3]+Data[2])+Data[1])+Data[0];
The result will be -1 if you use the -fwrapv flag or not. But is you do:
if(a<0)
printf("the value of a %d is negative\n",a);
else
printf("the value of a %d is positive\n",a);
It will print "the value of a -1 is positive" because when optimising it removes the first part because + - and * of positive numbers will always be positive (char is unsigned).
When you compile with the -fwrapv flag it will print the correct answer.
If you use this:
int a=(Data[3]<<24)+(Data[2]<<16)+(Data[1]<<8)+Data[0];
The code works as expected with or without the flag.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With