Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Avoiding interpretation as octal in a C macro

In an application I'm working on, we have a macro to convert clock times into an internal representation as a short integer:

#define CLOCK_TIME(hr, min) (s32)(((hr) * 60 + (min)) * 0x10000 / (24 * 60))

Naturally, we would write the hours and minutes exactly as they'd appear on a clock. So, for example, 6:30 would be CLOCK_TIME(6, 30). This all worked fine until someone tried to enter CLOCK_TIME(6, 08) and the compiler spat out an error saying 08 is not a valid octal constant. We tried a few workarounds, but they led to their own problems. For example, replacing min with 1##min - 100 fixed that specific issue, but then the macro would fail with any input that wasn't a 2-digit decimal literal. Is there a way we could allow 08 and 09 to work in the macro while also allowing arguments that may be single digits, expressions, or variables?

And yes, I realize just not using leading zeroes is a solution, but having the times appear the way they appear on a clock improves both readability and usability.

EDIT: One additional constraint is that if the time is a constant, it must be calculated at compile time and not runtime.

like image 210
eyeballfrog Avatar asked Oct 27 '25 22:10

eyeballfrog


2 Answers

A solution for any number of digits (within implementation limits) (but not expressions or variables) is 1##min*2 - 2##min. This can be thought of as (10p+x)•2 − (2•10p+x), where p is the unknown number of digits in x, and it equals (2•10p+2x)−(2•10p+x) = 2xx = x. This avoids floating-point issues.

For example, 23 would be replaced by 123*2 - 223, which equals 23. 08 would be 108*2 - 208 = 8, and 8 would be 18*2 - 28 = 8.

A solution that handles numerals, expressions, and variables is to write your own script outside of C. Humans are not required to limit their programs to the C preprocessor. You can write your own script to remove leading zeros from numerals inside invocations of this macro and then use this script to process the source code before compiling it.

like image 157
Eric Postpischil Avatar answered Oct 30 '25 14:10

Eric Postpischil


As @dimich suggested, you can append scientific notation e0.
This will first force the number to be a double, then you can type-cast it back to an int.

#define CLOCK_TIME(hr, min) (int32_t)(((hr) * 60 + (int32_t)(min##e0)) * 0x10000 / (24 * 60))

This works because 08e0 is valid as (double)8.000, even though 08 is invalid octal.

IDEOne Link

like image 24
abelenky Avatar answered Oct 30 '25 14:10

abelenky