0

I was wondering how I would shorten numbers, for example 10000000000 (ten 0s), to something shorter like 1e10, and how to implement this into my code. This is my current code:

do
{
    card_num = get_long("Number: ");
}
while (card_num < 0);

num1 = ((card_num % 100) / 10) * 2;
num2 = ((card_num % 10000) / 1000) * 2;
num3 = ((card_num % 1000000) / 100000) * 2;
num4 = ((card_num % 100000000) / 10000000) * 2;
num5 = ((card_num % 10000000000) / 1000000000) * 2;
num6 = ((card_num % 1000000000000) / 100000000000) * 2;
num7 = ((card_num % 100000000000000) / 10000000000000) * 2;
num8 = ((card_num % 10000000000000000) / 1000000000000000) * 2;

This is what I want it to look like (or similar):

do
{
    card_num = get_long("Number: ");
}
while (card_num < 0);

num1 = ((card_num % (1e2) / 1e1) * 2;
num2 = ((card_num % 1e4) / 1e3) * 2;
num3 = ((card_num % 1e6) / 1e5) * 2;
num4 = ((card_num % 1e8) / 1e7) * 2;
num5 = ((card_num % 1e10) / 1e9) * 2;
num6 = ((card_num % 1e12) / 1e11) * 2;
num7 = ((card_num % 1e14) / 1e13) * 2;
num8 = ((card_num % 1e16) / 1e15) * 2;

But this is the error message that I get:

credit.c:25:25: error: expected expression
    num1 = ((card_num % long(1e2) / 1e1) * 2;
                        ^
fatal error: too many errors emitted, stopping now [-ferror-limit=]
2 errors generated.
make: *** [<builtin>: credit] Error 1

How do I fix this?

  • 5
    Is that an [XY problem](http://xyproblem.info/)? If you enter the card number as a string (as you should) then you've already got a nice array of digits. A "number" isn't necessarily an "integer". – Weather Vane Feb 26 '22 at 17:52
  • If you have an integer like `int` or `long`, you can convert it to `double`. Or you can read a `double` in from the user directly. Or you can convert a string to a double using `atof` or `strtod`. Once you have a `double`, you can print it using `%e` format, and it will come out as, for example, `1e10`. – Steve Summit Feb 26 '22 at 18:02
  • Either `(long)1e2` or `1e2L` make C-Lang happy. People who have spent a long time with C might point fingers and moan about floats, but I did note gcc, clang do not complain about 'long x = 1.23e3L;' – mevets Feb 26 '22 at 18:03
  • 2
    @mevets MSVC issues a warning for that: *conversion from 'long double' to 'long', possible loss of data*. – Weather Vane Feb 26 '22 at 18:06
  • 4
    Since the file name is `credit.c` and I some references to `card`, I'm going to assume this might be about credit card numbers. Don't process credit card numbers unless you're PCI-DSS compliant. – Cheatah Feb 26 '22 at 18:16
  • 2
    Note that `long` may only be 32-bit and `get_long("Number: ");` is insufficient to cope with a 16 decimal digit number. Result: non-portable code. Look to `uintmax_t` or `unsigned long long` if still desirous of a integer solution. – chux - Reinstate Monica Feb 26 '22 at 18:16
  • @chux-ReinstateMonica _"`long` may only be 32-bit"_... Really? – Zakk Feb 26 '22 at 18:27
  • 1
    @Zakk Yes. Really. Once upon a time `short`, `int`, and `long` were 16, 16, and 32 bits. Then for a while it was often 16, 32, 32. These days, it's true, for many machines it's 16, 32, 64, which is a nice distribution, but it's not the only one. Type `long` is only guaranteed to be 32 bits. If you know you need 64, you should use `long long`, or `int64_t`. – Steve Summit Feb 26 '22 at 18:31
  • @SteveSummit So the safest way is to use `intX_t` to guarentee having an `X`-bit integer. – Zakk Feb 26 '22 at 18:36
  • @Zakk If you need exactly X bits, yes. – Steve Summit Feb 26 '22 at 18:38
  • However, `stdint.h` and `inttypes.h` are not guaranteed to be there until `C99`. Something to think about if you are worried about compatibility. – Neil Feb 26 '22 at 18:55
  • 1
    `long(1e2)` is not valid in C (it is valid in C++). In C you need `(long)1e2`. – n. m. could be an AI Feb 26 '22 at 20:44
  • 1
    @Neil "not guaranteed to be there until C99" in that case, integers wider than 32-bit (like 100000000000000) are not certain either. Another reason to abandon either pre-C99 or and an integer solution here. IMO, give up on pre-C99 compatibility - it has been 23 years - time to move on. – chux - Reinstate Monica Feb 26 '22 at 21:34

3 Answers3

1
#define KILO 1000U
#define KIBI 1024U
#define MEGA 1000000LLU
#define MEBI 1048576LLU
#define GIGA 1000000000LLU
#define GIBI 1073741824LLU

int a = 42 * KILO;
int b = -1 * MEGA;
long long unsigned mem = 24 * GIBI; // my machine has 24GBytes

kibi, mebi, gibi are the corresponding binary prefixes to kilo, mega, giga

pmg
  • 106,608
  • 13
  • 126
  • 198
  • 2
    Hmmm, `#define iGIGA (1LLU * iKILO * iMEGA)` seems to break the type pattern. Perhaps `#define iGIGA (1LL * iKILO * iMEGA)` or `#define uGIGA (1LLU * iKILO * iMEGA)`. Note: `(iKILO * iKILO)` risks overflow. – chux - Reinstate Monica Feb 26 '22 at 18:54
  • 1
    Rather than build up large values with small constants and incur type range issues, going direct avoids that: `#define iMEGA (1048576 /* 1024*12104 */) #define iGIGA 1073741824`. – chux - Reinstate Monica Feb 26 '22 at 18:57
  • @chux-ReinstateMonica are those numbers even guaranteed to be in the range the compiler understands? It might be best to have a string, instead. – Neil Feb 26 '22 at 19:13
  • @Neil I guess it's better if the code writer/caller knows whether the target architecture is 8-bit, 16-bit, 32-bit or 64-bit. For example: `#if defined ARCH32 #define MEGA ....` – Zakk Feb 26 '22 at 19:17
  • @Neil _Every_ compliant C compiler (C89 to date) understands 1073741824 as it is in the range of +/-(2**31 - 1). – chux - Reinstate Monica Feb 26 '22 at 19:47
  • pmg, Try `printf("%llu\n", -1 * MEGA);`. I get `18446744073708551616`. Assigning that to an `int` is implementation define behavior. – chux - Reinstate Monica Feb 26 '22 at 19:56
0

To add to @pmg's solution (with @chux's comment considered):

#define KB *1024
#define MB *1048576
#define GB *1073741824
// ...

Or:

#define KB *1000
#define MB *1000000
#define GB *1000000000
// ...

Example code:

printf("%ld", 1 MB); // 1048576 or 1000000
printf("%ld", 8 GB); // 8589934592 or 8000000000

This is far from a perfect solution, but if you want to avoid macros, you can use this function:

#include <stdio.h>
#include <string.h>
#include <ctype.h>

long exp_notation(const char *number)
{
    char *expo = strchr(number, 'e');
    if (!expo)
        return -1;
    
    const char *p = number;
    long ret = 0;
    long ex = 0;
    
    // Numbers before e
    for (; p != expo; ++p) {
        if (!isdigit(*p))
            return -1;
        
        ret *= 10;
        ret += *p - 48;
    }
    
    // Numbers after e
    for (p = expo+1; *p; ++p) {
        if (!isdigit(*p))
            return -1;
        
        ex *= 10;
        ex += *p - 48;
    }
    
    while (ex--)
        ret *= 10;
    
    return ret;
}

int main(void)
{
    printf("%ld\n", exp_notation("45e9")); // 45000000000
}

If the e doesn't exist in the input, it returns -1. Just make sure you pass the correct input.

EDIT: AS pointed by @chux, ff you are under a 32-bit machine then something like 8 GB will cause an undefined behaviour. See here and here to know which architecture your code is running on.

Zakk
  • 1,935
  • 1
  • 6
  • 17
  • `8 GB` is UB when `long` is 32-bit as that is signed integer overflow. `printf("%ld", 1 MB);` fails when `long` is 64-bit and `1 MB` is 32-bit `int`. – chux - Reinstate Monica Feb 26 '22 at 19:51
  • @chux-ReinstateMonica I guess it's up to the user to check if the target architecture is 32-bit or 64-bit. This answer (and of course pmg's answer) gives a hint on how to do it. One would use compiler/platform-specific macros to know the target architecture. I will update my answer and point to this detail. – Zakk Feb 26 '22 at 20:00
  • I'd recommend against "up to the user to check if the target architecture is 32-bit or 64-bit" and create code the does not rely on that distinction or steers code through macros. – chux - Reinstate Monica Feb 26 '22 at 21:29
0

Something similar to what you are asking for, would be the following:

#include<stdint.h>
...

num1 = ((card_num % (uint64_t)1e2) / (uint64_t)1e1) * 2;
num2 = ((card_num % (uint64_t)1e4) / (uint64_t)1e3) * 2;
(etc.)
nielsen
  • 5,641
  • 10
  • 27