patterncMinor
Safe, fully standards-following integer abs in C
Viewed 0 times
absfollowingstandardsfullysafeinteger
Problem
Here's a bit tricky one, because AFAIK there is no existing platform, where conditionally compiled part of the code would actually get included by compiler, so it is entirely up to the human review to see if it is correct.
The purpose is to have abs function, which does not trigger undefined behavior with any user input, on any C99 standard compliant compiler, on any platform, and in the theoretical case of result not fitting in the return type, return maximum value that fits.
Apart from any bugs, blunders etc, the real challenge is to come up with a (maybe theoretical) platform where the code doesn't produce desired result, or (hopefully) be reasonably certain that it works in all cases allowed by the standard.
The purpose is to have abs function, which does not trigger undefined behavior with any user input, on any C99 standard compliant compiler, on any platform, and in the theoretical case of result not fitting in the return type, return maximum value that fits.
Apart from any bugs, blunders etc, the real challenge is to come up with a (maybe theoretical) platform where the code doesn't produce desired result, or (hopefully) be reasonably certain that it works in all cases allowed by the standard.
// function to review
#include
uintmax_t maxed_abs(intmax_t i) {
if (i
int main(void)
{
printf("Enter int in range %jd .. %jd:\n > ", INTMAX_MIN, INTMAX_MAX);
intmax_t i;
if (scanf("%jd", &i) == 1) {
printf("Result: |%jd| = %ju\n", i, maxed_abs(i));
}
}Solution
-
This kind of code relies on careful reading of the standard, so if it were me I would add cross-references to the sections of the standard that I was relying on, for example I might write:
-
I don't like writing code that can't be tested on any platform that I have access to. Although the code looks good in this case, if you pursued this approach on a bigger scale, then it would be very surprising if all the untested code were correct. I would prefer to write something like:
so that untested platforms get a compiler error instead of compiling and executing untested code. In the unlikely event that you discover such a platform, you can add the code and test it at that point.
This kind of code relies on careful reading of the standard, so if it were me I would add cross-references to the sections of the standard that I was relying on, for example I might write:
/* This relies on the following facts from the C99 standard:
*
* §6.2.5.9 The range of nonnegative values of a signed integer
* type is a subrange of the corresponding unsigned integer type.
* It follows from this and §7.18.2.5 that UINTMAX_MAX >=
* INTMAX_MAX.
*
* §6.2.6.2.2 Signed integers are represented as sign-and-
* magnitude, two's complement, or one's complement. It follows
* that INTMAX_MIN is either -INTMAX_MAX (in sign-and-magnitude or
* one's complement) or -INTMAX_MAX-1 (in two's complement).
*/-
I don't like writing code that can't be tested on any platform that I have access to. Although the code looks good in this case, if you pursued this approach on a bigger scale, then it would be very surprising if all the untested code were correct. I would prefer to write something like:
#if UINTMAX_MAX == INTMAX_MAX && INTMAX_MIN < -INTMAX_MAX
#error "Two's complement with UINTMAX_MAX == INTMAX_MAX is not supported."
#endifso that untested platforms get a compiler error instead of compiling and executing untested code. In the unlikely event that you discover such a platform, you can add the code and test it at that point.
Code Snippets
/* This relies on the following facts from the C99 standard:
*
* §6.2.5.9 The range of nonnegative values of a signed integer
* type is a subrange of the corresponding unsigned integer type.
* It follows from this and §7.18.2.5 that UINTMAX_MAX >=
* INTMAX_MAX.
*
* §6.2.6.2.2 Signed integers are represented as sign-and-
* magnitude, two's complement, or one's complement. It follows
* that INTMAX_MIN is either -INTMAX_MAX (in sign-and-magnitude or
* one's complement) or -INTMAX_MAX-1 (in two's complement).
*/#if UINTMAX_MAX == INTMAX_MAX && INTMAX_MIN < -INTMAX_MAX
#error "Two's complement with UINTMAX_MAX == INTMAX_MAX is not supported."
#endifContext
StackExchange Code Review Q#119220, answer score: 3
Revisions (0)
No revisions yet.