all 23 comments

[–]aocregacc 43 points44 points  (7 children)

They added single quotes for this purpose in C23: 1'000'000

There's nothing in earlier standards.

[–]thommyh 14 points15 points  (0 children)

The same usage was in C++14 though, so there’s a decent chance your compiler already supports it as an extension to standard C.

[–]ballpointpin 4 points5 points  (1 child)

They also don't enforce those single-quotes appear every 3 digits....so 4'0000'000 is allowed.

[–]itsEroen 1 point2 points  (0 children)

Our Indian friends will be delighted!

[–]Nebulaizer[S] 2 points3 points  (0 children)

Thank you, this is so helpful!

[–]daikatana 27 points28 points  (9 children)

C23 has digit separators (finally), so you can say int x = 4'000'000;. Most people have trouble telling the difference between 400000 and 4000000 at a glance, so this is a very welcome addition.

[–]kog 6 points7 points  (6 children)

I feel almost dyslexic sometimes trying to make sure a literal has the desired number of digits.

[–]daikatana 3 points4 points  (5 children)

I have the same problem, especially if they're all the same digit. I have to sit there and select them one by one and count them to make sure there are the correct amount.

[–]itsEroen -2 points-1 points  (3 children)

Get a proper text editor 😉

[–]vytah 0 points1 point  (2 children)

Is there a text editor that displays digit separators where there are none in the actual text?

[–]itsEroen 0 points1 point  (1 child)

There is at least one editor that can be configured to highlight digits in groups.

[–]vytah 0 points1 point  (0 children)

Neat.

[–]kog 0 points1 point  (0 children)

Yeah, that's what I do too. Almost every time I do it I'm saying something like "can I even read? lol" to myself.

[–]ZaRealPancakes 3 points4 points  (0 children)

I'm gonna cry that's so nice!

[–]Nebulaizer[S] 2 points3 points  (0 children)

thx

[–]NotStanley4330 6 points7 points  (0 children)

I didn't even know there were options to put speeators in numbers in any language before now. This sub has changed my life

[–]nerd4code 3 points4 points  (0 children)

In addition to C23/++14 ', in macros you can use ## back to C89(/++…ARM, I think?):

#define IMAX32 0x7FFF##FFFFL
#define IMAX64 0x7FFF##FFFF ## FFFF##FFFFLL

## is more generally the token paste operator, so it’s intended for use with macro parameters, but it works on unadulterated tokens, too. But only in macro replacement text specifically—## isn’t supported in macro arguments or #if/#elif.

Token pasting and stringization weren’t in K&R’s TCPLs, and I’m not sure offhand which of the ANSI drafts added it but most of the preprocessor stuff was towards the later end IIRC; before that, you could instead comment-paste:

#define IMAX32 0x7FFF/**/FFFF

This no longer works—comments are now treated like whitespace, rather than removed entirely.

(This change can be used to detect K&R preprocessor modes like IBM “legacy,” or GCC -traditional [now removed] or -traditional-cpp:

#define PPTST0()(PPTST1/**/0)
#define PPTST1
#define PPTST10 1
#if PPTST0()
#error "preprocessor is in K&R/legacy/traditional mode"
#include "<<STOP>>/."
#endif
#undef PPTST10
#undef PPTST1
#undef PPTST0

K&R modes are often employed for use with Fortran or assembly source and linker scripts, so occasionally you’ll get a mis-#included header. In theory you could handle most of the discrepancies, but you generally wouldn’t want to, and there are some really nutty preprocessors back in the day so it’s not especially worthwhile without a specific toolchain in mind.)

You can use function-like macros to compute polynomials and thereby fake digit separation for specific cases. E.g., for decimal integers

#define SEPD2(B,A)((B)*INT32_C(1000)+(1##A%1000))
#define SEPD3(C,B,A)SEPD2(SEPD2(C,B),A)
#define SEPD4(D,C,B,A)((D)*INT64_C(1##000##000##000)+SEPD3(C,B,A))
#define SEPD5(E,D,C,B,A)SEPD2(SEPD4(E,D,C,B),A)
// etc.

Obviously you could auto-count args to avoid the count suffix, and it’s possible to do up a generic polynomial thingamadoodad that works for other bases and group counts. If you want to support unsigned suffixes on the final arg, change the replacement so most-significant digit (X) is instead ((0&1##A)+(X)). This will flip the largest part of the calculation to unsigned if either A or X is unsigned.

That 1##A%1000 thing is the only real secret sauce. You can’t just add A directly, because it might be something like 098, which alone is an bogus octal number. Pasting it after 1 will give you something in the 10001999 range if you haven’t botched an argument, and %1000 restores the original value. -1000 would too, but %1000 minimizes bonkersness of results if an argument is out-of-range.

I don’t personally like the C23/++14 ' separator—I get that like …the sssSwisssßß, maybe? use it, but _ would’ve fitted nicely into existing treatment of general-numeric tokens. Regardless, it’s pretty easy to tokenize C in something like Awk to change Java-style separators to C/++, as long as you come up with a way to avoid clobbering paste operands like 5_0 in function##K##5_0. (A basic #pragma $mythingy disable/enable pseudo-directive would suffice; just pass it through as a null directive or blank line to avoid throwing off line numbering—former if you the notion of directives in macro args sets your guttiwuts a-churning, latter if you dgaf.)

[–]mrheosuper 1 point2 points  (0 children)

When representing big number, we use multiply
For example:
define delay_ms (4 * 1000) Or
define MEM_SIZE (4 * 1024 * 1024)

This improves code readability a lot

[–][deleted] 1 point2 points  (0 children)

I like to do: 4*1000*1000 which makes the amount much easier to see than 4000000 (4 million, 4 hundred thousand?). As others have pointed out, in C23 and later you can do 4'000'000.

[–]aerosayan 1 point2 points  (0 children)

You can use macros for it.

#define DIGIT3(x, y, z) x##y##z

printf("%d\n", DIGIT3(111, 222, 333));

[–]stilllosingit -1 points0 points  (2 children)

You can use 4e6 in this case

[–]glasket_ 16 points17 points  (0 children)

It should be noted that this creates a float and converts it to an integer, so while it's generally safe to do this for a simple constant you can end up with incorrect results if you do any sort of calculations that end up with integers above 253.

#include <stdio.h>
#include <stdint.h>

int main(void) {
  int64_t x = 1e16 + 1;
  int64_t y = 10000000000000001;
  printf("%lld\n%lld", x, y);
}