C: the long and short of it - Page 1
Forum section: electronics / general

author: PerranOak
author_rank: Frequent Contributor
timestamp: May 12, 2020, 05:11:27 pm
content: I am using MPLAB X IDE v5.25, XC8 v2.10 and a PIC16F1827.I needed a large integer to calculate an area (an exercise) and so used:unsigned long int areaI couldn't understand the result until I investigated and found thatareawas only 16-bit not, as I had expected, 32-bit.Am I missing something?Cheers

author: SiliconWizard
author_rank: Super Contributor
timestamp: May 12, 2020, 06:06:01 pm
content: The XC8 manual defines "unsigned long" as a 32 bit unsigned integer, so unless there's a big bug with your compiler's version, your problem is not with the width of the 'area' variable.It's very likely that it's a problem with the expression you're using to compute the area, related to how C handles integer promotion. Please post the line of code that computes it.

author: andersm
author_rank: Super Contributor
timestamp: May 12, 2020, 09:31:05 pm
content: Remember that the data type that's on the left side of the assignment operator has absolutely no influence over how the right-hand side is calculated.

author: brucehoult
author_rank: Super Contributor
timestamp: May 13, 2020, 01:20:41 am
content: Quote from: andersm on May 12, 2020, 09:31:05 pmYup. If HEIGHT and WIDTH are 16 bit variables then to get a 32 bit area you need to cast at least one of them to long. Otherwise all arithmetic (for char, short, int) is done at int precision.Code:area = (unsigned long)height * width;

author: PerranOak
author_rank: Frequent Contributor
timestamp: May 13, 2020, 12:47:16 pm
content: I reduced the code to its essential elements:#include <xc.h>#include "stdio.h"void main(void){unsigned int radius;unsigned long int area;unsigned char outstring;while(1==1){radius = 25;area = (radius * radius * 31416) / 10000;sprintf( outstring, "Area=%u", area);lcdputs( outstring );}}Then, I changed "radius" tolong intand, hey presto, it worked!Thank you very much guys, I'd never have thought of that in a month of Sundays.

author: SiliconWizard
author_rank: Super Contributor
timestamp: May 13, 2020, 01:33:48 pm
content: That's a very common trap related to how C deals with integers.I recommend reading this:https://wiki.sei.cmu.edu/confluence/display/c/INT02-C.+Understand+integer+conversion+rulesAnd all this actually:https://wiki.sei.cmu.edu/confluence/display/c/SEI+CERT+C+Coding+StandardDepending on what you do, you don't have to follow all of these rules, but they will certainly teach you a lot anyway.

author: Jeroen3
author_rank: Super Contributor
timestamp: May 13, 2020, 01:50:07 pm
content: Yeah, C integer promotions are tricky. Please note that literals, like the 31416 are at leastintunless otherwise specified.Code:integer-suffix:unsigned-suffix long-suffixoptunsigned-suffix long-long-suffixlong-suffix unsigned-suffixoptlong-long-suffix unsigned-suffixoptunsigned-suffix: one ofu Ulong-suffix: one ofl Llong-long-suffix: one ofll LLLYou may also be interested in theQ number formatandlibfixmathto stop scattering around those division by 10000.

author: mrflibble
author_rank: Super Contributor
timestamp: May 30, 2020, 01:57:08 pm
content: Title fixed.

author: Nominal Animal
author_rank: Super Contributor
timestamp: May 30, 2020, 09:09:52 pm
content: As an aside, 355/113 ≃ 3.14159292 gives you Pi with six/seven digits of precision, and it approximates Pi best among ratios of two sixteen bit (signed or unsigned) integers. With 24-bit (signed or unsigned) integers, 5419351/1725033 ≃ 3.141592653589815 gives you 12/13 digits. With eight (unsigned) bits, the best is 245/78 ≃ 3.141025641, just four digits of precision. Below that, the best is 22/7 ≃ 3.142857, just three digits.On an 8-bit or 16-bit microcontroller, Q15 or Q23 (QN) work well for angles. As an integer,x= 2N·degrees/180 orx= 2N·radians/π with modulo arithmetic, so 2Nand -2Ndescribe the same angle. Trigonometric functions can then be implemented via CORDIC, and you won't need π for anything (except when building the constant tables, but you do that at compile time on a host computer anyway). You get the same or better overall precision as floats, but with much faster operations, and saving a byte per variable too. Things like versors (unit quaternions describing rotations, or orientations in 3D space; analogous to rotation matrices, but with much nicer features) can use four Q23 components, for a total of 12 bytes, nicely. Then, the matrix you generate from the versor can use Q15.Q16 or even Q23.8, depending on the scale/range of your coordinates, without compounding rounding errors causing any grief.

author: brucehoult
author_rank: Super Contributor
timestamp: May 31, 2020, 04:52:47 am
content: Quote from: Nominal Animal on May 30, 2020, 09:09:52 pmAnd with 30 to 34 bit integers you won't do better than 817696623/260280919 = 3.141592653589793 gives three more digits -- basically full DP.3.14159265358979311118 817696623/2602809193.14159265358979323846 pi3.14159265358981538324 5419351/1725033

author: chriva
author_rank: Regular Contributor
timestamp: May 31, 2020, 05:45:41 am
content: This is way overkill if your compiler can emulate 32-bit operations and you don't need more but it's quite easy to extend the number of bits for multiplication, addition and subtraction if you know assembler.as long as you can detect carry, you're basically only memory bound.Division is ofc also possible but it becomes dirty, real fast. Not fun unless you really, really need it.

author: westfw
author_rank: Super Contributor
timestamp: May 31, 2020, 07:26:52 am
content: QuoteAlmost on topic: Not in C (or C++), which will compute 355/113 as integer division and give you 3. Better use "355.0/113", or M_PI that is defined off in math.h

author: Siwastaja
author_rank: Super Contributor
timestamp: May 31, 2020, 02:21:07 pm
content: Quote from: westfw on May 31, 2020, 07:26:52 amObviously, you would use 355/113 in a limited-resolution fixed point system (as explained by Nominal Animal); if you have floating point available and can use it, there is absolutely no point using 355.0/113, but use M_PI instead.In fixed point math using 355/113, you need to use parenthesis to make sure the multiplication by 355 is done first with the input value, then result divided by 113; if the calculation order is wrong, 355/113 will indeed evaluate to 3. Further, you need to manually verify the intermediate multiplication fits the range of variables you are using, or provide necessary typecasting to make it fit.

author: SiliconWizard
author_rank: Super Contributor
timestamp: June 01, 2020, 03:35:43 pm
content: Yes obviously, that's for fixed-point use.The matter in general is related to how to handle integer multiply and divide properly. Not specific to this example, and important to know.Example: you just need to compute: n * 2 / 3In many languages, if you write it like this, it will yield zero, and using parentheses is required: (n * 2) / 3. This is very basic programming knowledge. And in this small example, you may not even quite realize you're dealing with fixed point (although it kind of is.)

author: kjpye
author_rank: Contributor
timestamp: June 02, 2020, 08:36:16 am
content: Quote from: SiliconWizard on June 01, 2020, 03:35:43 pmReally? Which languages are those? All of the (relatively few) languages I know use the usual rules of mathematics for such expressions, and the multiplications and divisions are performed left to right, so the multiplication is done before the division.

author: ehughes
author_rank: Frequent Contributor
timestamp: June 02, 2020, 02:52:35 pm
content: One thing that I have found helpful is abandon the use of int, long, etc with *any* compiler. Use stdint.h types. uint8_t, in32_t, etc.It makes tracking things down a bit easier.

author: TK
author_rank: Super Contributor
timestamp: June 02, 2020, 03:38:44 pm
content: Quote from: ehughes on June 02, 2020, 02:52:35 pmEspecially when you are programming microcontrollers, it makes more sense to use uintN_t than the ambiguous int, long, short, etc.

author: chriva
author_rank: Regular Contributor
timestamp: June 02, 2020, 04:05:41 pm
content: More often than not, you don't have stdint, intdef and so on.typedef that mofo and check default behaviour for that particular platforms and compiler. Some compiler flags will also change default sizes of things so be wary

author: SiliconWizard
author_rank: Super Contributor
timestamp: June 02, 2020, 05:26:25 pm
content: Quote from: kjpye on June 02, 2020, 08:36:16 amI was thinking about C first, of course, but also in general languages in which * and / have the same precedence, and the order of evaluation is not guaranteed.But you're making me question the relevance of my small example. I've just tested (with GCC -O3), and 'n * 2 / 3' yields the exact same code as '(n * 2) / 3'. Now this is of course no proof that this will always be the case with all compilers, all expressions, all contexts, etc. I've taken a look at the C standard (C99), and I haven't clearly seen stated that the order of operations for operators with the same precedence is guaranteed to be left to right. (I may have missed it, so if there's someone - maybe like Nominal Animal - that knows the standard well and can point me to that, I'll take it!)I've built a habit of explicitely using parentheses in order not to make any assumption about the order of evaluation (except of course when the order is certain from the precedence of operators).Now a better example (more likely to be *really* problematic in C) would be when adding integer promotion rules in the mix.

author: SiliconWizard
author_rank: Super Contributor
timestamp: June 02, 2020, 05:27:59 pm
content: Quote from: ehughes on June 02, 2020, 02:52:35 pmWhen you need an explicit fixed size, or at least a minimum guaranteed size that is not implementation-defined, yes, absolutely!But keep in mind that using stdint types doesn't mitigate per se the 'integer promotion' issues you can encounter when dealing with integer arithmetic in C, so you still need to be careful with that.

author: tggzzz
author_rank: Super Contributor
timestamp: June 02, 2020, 05:52:20 pm
content: Quote from: kjpye on June 02, 2020, 08:36:16 amThe operator precedence is completely separate from the order of evaluation, especially with C/C++ compilation with optimisations turned on.There is no concept of left-to-right or right-to-left evaluation in C, which is not to be confused with left-to-right and right-to-left associativity of operators: the expression f1() + f2() + f3() is parsed as (f1() + f2()) + f3() due to left-to-right associativity of operator+, but the function call to f3 may be evaluated first, last, or between f1() or f2() at run time.https://en.cppreference.com/w/c/language/eval_orderIn my experience most programmers that think they know C, don't. Iknowthat I don't know C anymoreFor amysement, you could also look at APL; athttps://tryapl.org/try typing in each of these expressions, and explain the results7 - 77 - 7 - 77 - 7 - 7 - 77 - 7 - 7 - 7 - 7

author: Nominal Animal
author_rank: Super Contributor
timestamp: June 03, 2020, 05:50:10 am
content: To do fixed-point multiplication in C, you almost always need to add proper casting. The one special case is when you know (from prior checking or casting) that the product cannot overflow. The same applies to fixed-point division, because it starts with a multiplication (by the integer value corresponding to 1.0 in fixed-point).As an example, here is a function that multiplies a 16-bit signed integer (assumed to be not greater than 10430 in magnitude) by Pi.Code:int16_t mul_pi16(const int16_t v) {return ((int32_t)v * 355) / 113; /* Note: 355/113 is the best 16:16-bit approximation for Pi */}By the way, there are exactly 105 32-bit signed integer ratios (210 if you include negative numerator and denominators) matching Pi in IEEE 754-Binary64 ("double"). My test program is still comparing them against IEEE-754 Binary64 Pi (on operations over all 32-bit signed integers), and it is already 3.4% done; I'll report if it yields any practical results.In C99 and later, casting to a numeric type (integer or floating-point type) has much stricter definition than in ANSI C/C89: the cast value is limited to the range and precision of the type it is cast to. This means that a cast in an expression is equivalent to using a temporary variable of that type, no matter what optimizations are in use (except those that break the standard, of course).I am not a language lawyer, and much prefer real world experience to the standard text, but since none of the standard arithmetic operators (+, -, *, /, bit shifts) are sequence points, the compiler is free to evaluate such in any order it chooses, as long as the mathematical rules of precedence are followed. By that I mean that givena*b - c*d, the compiler is free to choose which product it computes first. (In C99 n1256, this is stated in 6.5p3: "[Except for logical operators, conditional operator, and function call parameters], the order of evaluation of subexpressions and the order in which side effects take place are both unspecified.")Parentheses in an expression (outside function call parameter list, which lexically is parenthesised) are not a sequence point either, so they are only used in their mathematical sense, and do not define the order in which the compiler must evaluate the subexpressions. In particular, an expression such as(a*(b-c))*ddoes not mean that the compiler has to calculate(b-c)first, then multiply it bya, and finally byd; the compiler is free to choose the evaluation order, as long as the math rules are not violated.

author: Nominal Animal
author_rank: Super Contributor
timestamp: June 03, 2020, 09:09:57 am
content: The report came in. Among 32-bit signed integer ratios, there are two particularly useful: 908679182/289241567 and 2025273829/644664682. The former is better when rounding down or truncating, the latter is better when rounding towards nearest integer. In hexadecimal, these are 0x3629580e/0x113d79df and 0x78b739e5/0x266ccd6a, respectively.The following assumeint32_t x;Instead of(int32_t)((double)x * M_PI), you can use((int64_t)x * 908679182) / 289241567It gives the same answer for almost allx– differs by 1 only in 109 cases among all nonnegative 32-bit signed integerx.Instead of(int32_t)((double)x / M_PI), you can use((int64_t)x * 644664682) / 2025273829It gives the same answer for almost allx– differs by 1 only in 13 cases among all nonnegative 32-bit signed integerx.Instead of(int32_t)round((double)x * M_PI), you can use((int64_t)x * 908679182 + 144620783) / 289241567It gives the same answer for almost allx– differs by 1 only in 104 cases among all nonnegative 32-bit signed integerx.(For negativex, you must use-144620783instead.)Instead of(int32_t)round((double)x / M_PI), you can use((int64_t)x * 644664682 + 1012636914) / 2025273829It gives the same answer for almost allx– differs by 1 only in 13 cases among all nonnegative 32-bit signed integerx.(For negativex, you must use-1012636914instead.)I haven't bothered to check the 13 cases when rounding to see which one is actually better, the floating-point one or the integer approximation. In all tests with rounding, I explicitly used half the divisor; should have checked with +1 since the divisors are odd. Also, I forgot to check forceil()cases.

author: Nominal Animal
author_rank: Super Contributor
timestamp: June 03, 2020, 01:12:02 pm
content: I wrote a couple of programs that can be used to find and evaluate integer ratios.The first one is a POSIXy one that uses Python3 to evaluate an expression (so that you can run it with e.g.'cos(22.5*pi/180)'as a parameter, then finds the best 32-bit signed integer ratio to approximate it:Code:#define POSIX_C_SOURCE 200809L#include <stdlib.h>#include <unistd.h>#include <sys/types.h>#include <sys/wait.h>#include <fcntl.h>#include <string.h>#include <stdio.h>#include <math.h>#include <errno.h>#define MAX_RATIOS 1000int safefd(int fd){unsigned int closemask = 0U;int saved_errno = errno;while (fd == STDIN_FILENO || fd == STDOUT_FILENO || fd == STDERR_FILENO) {closemask |= 1 << fd;fd = dup(fd);if (fd == -1) {saved_errno = errno;break;}}if (closemask & (1 << STDIN_FILENO)) close(STDIN_FILENO);if (closemask & (1 << STDOUT_FILENO)) close(STDOUT_FILENO);if (closemask & (1 << STDERR_FILENO)) close(STDERR_FILENO);errno = saved_errno;return fd;}int devnull(const int fd, const int flags){int newfd;/* Sanity check. */if (fd == -1)return errno = EBADF;if (flags & O_CREAT)return errno = EINVAL;newfd = open("/dev/null", flags);if (newfd == -1)return errno;if (newfd != fd) {if (dup2(fd, newfd) == -1) {const int saved_errno = errno;close(newfd);return saved_errno;}if (close(newfd) == -1) {const int saved_errno = errno;close(fd);return saved_errno;}}return 0;}static inline int writeall(const int fd, const void *const buf, const size_t len){const char *ptr = buf;const char *const end = (const char *)buf + len;ssize_t n;if (fd == -1)return errno = EBADF;while (ptr < end) {n = write(fd, ptr, (size_t)(end - ptr));if (n > 0) {ptr += n;} elseif (n != -1) {return errno = EIO;} elseif (errno != EINTR) {return errno;}}return 0;}int execute(const char *name, char *const argv[], const char *override_locale, pid_t *pid_to, int *output_fd){int status_pipe = { -1, -1 };int output_pipe = { -1, -1 };int error;pid_t child, p;/* Sanity checks. */if (!name || !*name || !argv || !argv)return errno = EINVAL;/* Clear pid_to and output_fd, if specified. */if (pid_to)*pid_to = (pid_t)-1;if (output_fd)*output_fd = -1;/* Construct output pipe. */if (pipe(output_pipe) == -1) {error = errno;return errno = (error ? error : EIO);}/* Construct status pipe. */if (pipe(status_pipe) == -1) {error = errno;close(output_pipe);close(output_pipe);return errno = (error ? error : EIO);}child = fork();if (!child) {/* Child process. *//* Close parent (read) ends of the pipes. */close(status_pipe);close(output_pipe);/* Make sure status pipe write end does not overlap a standard descriptor. */status_pipe = safefd(status_pipe);if (status_pipe == -1) {close(output_pipe);exit(99);}if (output_fd) {/* Move write end of output pipe to standard output. */if (dup2(output_pipe, STDOUT_FILENO) == -1) {error = errno;writeall(status_pipe, &error, sizeof error);close(status_pipe);exit(99);}if (close(output_pipe) == -1) {error = errno;close(STDOUT_FILENO);writeall(status_pipe, &error, sizeof error);close(status_pipe);exit(99);}} else {/* Discard all output by redirecting it to /dev/null. */close(output_pipe);if (devnull(STDOUT_FILENO, O_WRONLY)) {error = errno;writeall(status_pipe, &error, sizeof error);close(status_pipe);exit(99);}}/* Mark status pipe write end close-on-exec. */if (fcntl(status_pipe, F_SETFD, FD_CLOEXEC) == -1) {error = errno;close(STDOUT_FILENO);writeall(status_pipe, &error, sizeof error);close(status_pipe);exit(99);}/* Redirect standard input and error to /dev/null. */if (devnull(STDIN_FILENO, O_RDONLY) ||devnull(STDERR_FILENO, O_WRONLY)) {error = errno;close(STDOUT_FILENO);writeall(status_pipe, &error, sizeof error);close(status_pipe);exit(99);}if (override_locale) {setenv("LANG", override_locale, 1);setenv("LC_ALL", override_locale, 1);}/* Execute. */execvp(name, argv);error = errno;close(STDOUT_FILENO);writeall(status_pipe, &error, sizeof error);close(status_pipe);exit(99);}/* Parent process. *//* Close child ends of pipes. */close(status_pipe);close(output_pipe);/* Read status pipe. */{char buffer[2 * sizeof (int)];size_t have = 0;ssize_t n;error = 0;while (have < sizeof buffer) {n = read(status_pipe, buffer + have, sizeof buffer - have);if (n > 0) {have += n;} elseif (n == 0) {break;} elseif (n != -1) {error = EIO;break;} elseif (errno != EINTR) {error = errno;break;}}if (!error && have == sizeof error)memcpy(&error, buffer, sizeof error);close(status_pipe);if (have > 0) {/* Failed. Make sure we have an error code. */if (!error)error = ENOTSUP;/* Reap child process. */do {p = waitpid(child, NULL, 0);} while (p == -1 && errno == EINTR);return errno = error;}}/* Exec successful. */if (output_fd) {*output_fd = output_pipe;} else {close(output_pipe);}if (pid_to) {*pid_to = child;}return errno = 0;}const char expr_prefix[] ="from math import *\n""print(\"%.32g\\n\" % (";#define EXPR_PREFIX_LEN (sizeof expr_prefix - 1)const char expr_suffix[] ="))\n";#define EXPR_SUFFIX_LEN (sizeof expr_suffix - 1)int evaluate(const char *expr, double *to){const size_t expr_len = (expr) ? strlen(expr) : 0;char *expression, *ptr;char *argv;int result, error, fd = -1;pid_t child = -1;if (expr_len < 1)return errno = EINVAL;expression = malloc(EXPR_PREFIX_LEN + expr_len + EXPR_SUFFIX_LEN + 1);if (!expression)return errno = ENOMEM;ptr = expression;memcpy(ptr, expr_prefix, EXPR_PREFIX_LEN);ptr += EXPR_PREFIX_LEN;memcpy(ptr, expr, expr_len);ptr += expr_len;memcpy(ptr, expr_suffix, EXPR_SUFFIX_LEN);ptr += EXPR_SUFFIX_LEN;*ptr = '\0';argv = "python3";argv = "-c";argv = expression;argv = NULL;result = execute("python3", argv, "C", &child, &fd);if (result)return errno = result;free(expression);error = 0;{char buffer;size_t have = 0;ssize_t n;while (have < sizeof buffer) {n = read(fd, buffer + have, sizeof buffer - have);if (n > 0)have += n;elseif (n == 0)break;elseif (n != -1) {error = EIO;break;} elseif (errno != EINTR) {error = errno;break;}}if (!error && have > 0 && have < sizeof buffer) {double value;char *end = buffer;buffer = '\0';errno = 0;value = strtod(buffer, &end);if (errno) {error = errno;} elseif (end == buffer) {error = EINVAL;} elseif (*end != '\t' && *end != '\n' && *end != '\v' &&*end != '\f' && *end != '\r' && *end != ' ') {error = EINVAL;}if (!error && !isfinite(value))error = EDOM;if (!error && to)*to = value;}}/* Reap child process. */{int status = 0;pid_t p;do {p = waitpid(child, &status, 0);} while (p == -1 && errno == EINTR);if (p == -1) {if (!error)error = errno;}if (!error) {if (!WIFEXITED(status) || WEXITSTATUS(status))error = EINVAL;}}/* Done. */return errno = error;}struct ratio {int n;int d;};static double ratio_delta = HUGE_VAL;static size_t ratios_max = 0;static size_t ratios = 0;struct ratio *ratio = NULL;static inline void clear_ratios(void){ratios = 0;}static inline void add_ratio(const int n, const int d){if (ratios >= ratios_max) {ratios_max = (ratios | 32767) + 32769;ratio = realloc(ratio, ratios_max * sizeof ratio);if (!ratio) {fprintf(stderr, "\rOut of memory.\n");exit(EXIT_FAILURE);}}ratio.n = n;ratio.d = d;ratios++;}static double target = 0.0;static inline void check(const int n, const int d){if (d != 0) {const double delta = fabs(target - (double)((double)n / (double)d));if (delta < ratio_delta) {clear_ratios();ratio_delta = delta;add_ratio(n, d);} elseif (delta == ratio_delta) {size_t i;/* Check if a duplicate ratio (common factor) */for (i = 0; i < ratios; i++)if (n > ratio.n) {const unsigned int factor = n / ratio.n;if (ratio.n * factor == n && ratio.d * factor == d)return;} elseif (n < ratio.n) {const unsigned int factor = ratio.n / n;if (n * factor == ratio.n && d * factor == ratio.d)return;} else {if (d == ratio.d)return;}/* No, new one; add. */add_ratio(n, d);}}}static int parse_uint(const char *s, unsigned int *to){const char *z = s;unsigned long ul;unsigned int u;if (!s || !*s)return -1;errno = 0;ul = strtoul(s, (char **)&z, 0);if (errno)return -1;if (z == s)return -1;while (*z == '\t' || *z == '\n' || *z == '\v' ||*z == '\f' || *z == '\r' || *z == ' ')z++;if (*z)return -1;u = (unsigned int)ul;if ((unsigned long)u != ul)return -1;if (to)*to = u;return 0;}int main(int argc, char *argv[]){unsigned int nmax = 2147483647;unsigned int n;size_t i;if (argc < 1) {fprintf(stderr, "No.\n");exit(EXIT_FAILURE);}if (argc < 2 || argc > 3 || (argc > 1 && (!strcmp(argv, "-h") || !strcmp(argv, "--help")))) {fprintf(stderr, "\n");fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv);fprintf(stderr, " %s MATH-EXPRESSION [ MAX-NUMERATOR ]\n", argv);fprintf(stderr, "\n");fprintf(stderr, "This program uses Python3 to evaluate MATH-EXPRESSION\n");fprintf(stderr, "(with all of math imported to local namespace),\n");fprintf(stderr, "and then finds the best 32-bit ratio to describe the value.\n");fprintf(stderr, "\n");exit(EXIT_FAILURE);}if (argc > 2) {if (parse_uint(argv, &nmax) || nmax < 1) {fprintf(stderr, "%s: Invalid maximum numerator.\n", argv);return EXIT_FAILURE;}}if (evaluate(argv, &target)) {fprintf(stderr, "%s: %s.\n", argv, strerror(errno));exit(EXIT_FAILURE);}fprintf(stderr, "Finding best approximation for value %.24g:\n", target);fflush(stderr);for (n = 0; n <= nmax; n++) {int d = (int)((double)n / target);check(n, d - 1);check(n, d);check(n, d + 1);if (!(n & 16777215)) {fprintf(stderr, "\r%5.1f%% done", 100.0 * (double)n / (double)nmax);fflush(stderr);}}if (ratios < 1) {fprintf(stderr, "\rNo approximations found.\n");return EXIT_FAILURE;} else {size_t maxi;if (ratio_delta > 0.0)fprintf(stderr, "\rDone. Found %zu approximations:\n", ratios);elsefprintf(stderr, "\rDone. Found %zu approximations to within double precision:\n", ratios);fflush(stderr);maxi = ratios;if (maxi > MAX_RATIOS)maxi = MAX_RATIOS;for (i = 0; i < maxi; i++)printf("%zu. %10d / %10d (error %+.24g)\n", i + 1, ratio.n, ratio.d, (double)ratio.n / (double)ratio.d - target);}return EXIT_SUCCESS;}The second is a standard C one that examines a specified ratio among all 32-bit integer multiplicands (where the result does not overflow):Code:#include <stdlib.h>#include <inttypes.h>#include <stdio.h>#include <string.h>#include <errno.h>#include <math.h>static inline const char *skip_spaces(const char *s){if (s)while (*s == '\t' || *s == '\n' || *s == '\v' ||*s == '\f' || *s == '\r' || *s == ' ')s++;return s;}static int parse_double(const char *s, double *to){const char *end = NULL;double val;if (!s || !*s)return -1;errno = 0;val = strtod(s, (char **)&end);if (errno)return -1;if (!isfinite(val))return -1;if (!end || end == s)return -1;end = skip_spaces(end);if (*end)return -1;if (to)*to = val;return 0;}static int parse_uint(const char *s, unsigned int *to){const char *end = NULL;unsigned long ul;unsigned int u;if (!s || !*s)return -1;errno = 0;ul = strtoul(s, (char **)&end, 0);if (errno)return -1;if (!end || end == s)return -1;end = skip_spaces(end);if (*end)return -1;u = (unsigned int)ul;if ((unsigned long)u != ul)return -1;if (to)*to = u;return 0;}int main(int argc, char *argv[]){unsigned int nall = 0;unsigned int ntrunc = 0, overtrunc = 0, undertrunc = 0;unsigned int nround = 0, overround = 0, underround = 0;unsigned int nfloor = 0, overfloor = 0, underfloor = 0;unsigned int nceil = 0, overceil = 0, underceil = 0;unsigned int imax = 2147483647;unsigned int o = 0;unsigned int i, n, d, lmax;const char *valstr;double value;if (argc < 1) {fprintf(stderr, "No.\n");return EXIT_FAILURE;}if (argc < 4 || argc > 6 || !strcmp(argv, "-h") || !strcmp(argv, "--help")) {fprintf(stderr, "\n");fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv);fprintf(stderr, " %s TARGET N D [ OFFSET [ MAX ] ]\n", argv);fprintf(stderr, "\n");fprintf(stderr, "This program calculates\n");fprintf(stderr, " (int)( ((int64_t)x * N + OFFSET) / d)\n");fprintf(stderr, "for all nonnegative 32-bit signed integers, or up to MAX,\n");fprintf(stderr, "and reports its differences to\n");fprintf(stderr, " (int)(TARGET * x)\n");fprintf(stderr, " (int)floor(TARGET * x)\n");fprintf(stderr, " (int)ceil(TARGET * x)\n");fprintf(stderr, " (int)round(TARGET * x)\n");fprintf(stderr, "\n");return EXIT_FAILURE;}if (parse_double(argv, &value) || value <= 0.0) {fprintf(stderr, "%s: Invalid target floating-point value. Must be positive.\n", argv);return EXIT_FAILURE;}valstr = argv;if (parse_uint(argv, &n) || n < 1) {fprintf(stderr, "%s: Invalid numerator (N). Must be a nonnegative integer.\n", argv);return EXIT_FAILURE;}if (parse_uint(argv, &d) || d < 1) {fprintf(stderr, "%s: Invalid denominator (D). Must be a nonnegative integer.\n", argv);return EXIT_FAILURE;}if (argc > 4) {if (parse_uint(argv, &o)) {fprintf(stderr, "%s: Invalid offset.\n", argv);return EXIT_FAILURE;}}if (argc > 5) {if (parse_uint(argv, &imax) || imax < 1) {fprintf(stderr, "%s: Invalid maximum integer to examine.\n", argv);return EXIT_FAILURE;}}if (value > 1.0) {lmax = (unsigned int)ceil(2147483648.0 / value) - 1;if (imax > lmax)imax = lmax;}for (i = 0; i <= imax; i++) {const int target_trunc = (int)(value * (double)i);const int target_floor = (int)floor(value * (double)i);const int target_ceil = (int)ceil(value * (double)i);const int target_round = (int)round(value * (double)i);const int approx = (int)(((int64_t)i * n + o) / d);nall++;ntrunc += (approx == target_trunc);overtrunc += (approx > target_trunc);undertrunc += (approx < target_trunc);nround += (approx == target_round);overround += (approx > target_round);underround += (approx < target_round);nfloor += (approx == target_floor);overfloor += (approx > target_floor);underfloor += (approx < target_floor);nceil += (approx == target_ceil);overceil += (approx > target_ceil);underceil += (approx < target_ceil);if (!(i & 16777215)) {fprintf(stderr, "\r%5.1f%% done", 100.0 * (double)i / (double)imax);fflush(stderr);}}fprintf(stderr, "\rDone. \n");fflush(stderr);printf("(int)(((int64_t)x * %u + %u) / %u) == (int)(%s * x):\n", n, o, d, valstr);printf(" %u correct (%.3f%%), %u differences (%u under, %u over)\n", ntrunc, 100.0*(double)ntrunc/(double)nall, nall-ntrunc, undertrunc, overtrunc);printf("\n");printf("(int)(((int64_t)x * %u + %u) / %u) == (int)floor(%s * x):\n", n, o, d, valstr);printf(" %u correct (%.3f%%), %u differences (%u under, %u over)\n", nfloor, 100.0*(double)nfloor/(double)nall, nall-nfloor, underfloor, overfloor);printf("\n");printf("(int)(((int64_t)x * %u + %u) / %u) == (int)ceil(%s * x):\n", n, o, d, valstr);printf(" %u correct (%.3f%%), %u differences (%u under, %u over)\n", nceil, 100.0*(double)nceil/(double)nall, nall-nceil, underceil, overceil);printf("\n");printf("(int)(((int64_t)x * %u + %u) / %u) == (int)round(%s * x):\n", n, o, d, valstr);printf(" %u correct (%.3f%%), %u differences (%u under, %u over)\n", nround, 100.0*(double)nround/(double)nall, nall-nround, underround, overround);printf("\n");return EXIT_SUCCESS;}With these, in just a couple of minutes, I can say that to approximate cos(22.5*pi/180) ≃ 0.923879532511286738483136, there are 156 possible ratios among 32-bit integers. Of these, the first one (smallest numerator),(int)(((int64_t)x * 126426688 + 68421637) / 136843261) == (int)round(0.923879532511286738483136 * x)is almost always true for positive 32-bit signed integerx. It is not true for only 58 differentx; of which 27 times the left side is one less, and 31 times one greater than the right side.

author: westfw
author_rank: Super Contributor
timestamp: June 04, 2020, 12:44:05 am
content: QuoteMaybe. You should probably be using "size_t", "uint_leastN_t", or "int_fastN_t", too...The percentage of CPUs where "int" is an inefficient type for something like:Code:for (int i=0; i < 100; i++) { foo(myarray); /* stuff */ }is becoming pretty small.

author: SiliconWizard
author_rank: Super Contributor
timestamp: June 05, 2020, 01:53:14 pm
content: Quote from: Nominal Animal on June 03, 2020, 05:50:10 amThanks for pointing this out. If I'm not mistaken, I think this is what C99 standard calls "grouping"? And admittedly this part is not the clearest piece of text I've ever read.As you said though, casting a sub-expression WILL force the compiler to evaluate it as grouped.So in your example, if you write: '(a*(int32_t)(b-c))*d', for instance, then 'b-c' will be evaluated in this order.For fixed-point implementation, it is common to cast to a wider type for intermediate values, so using a cast in this context guarantees the order of evaluation.