The Phone Number Value

As a string, a phone number is easy to parse, left-to-right, as shown in last week’s Lesson. As a value, however, the processing requires mathematical manipulation to work it from left-to-right.

While both numbers and values are read from left-to-right (in English), it’s easier to process a number from right-to-left. The reason is that numbers can be any length, yet their first digit is always on the far left. Even when a you (a human, I’m supposing) reads a number, you confirm its length before you start reading. This is how you know whether to start in the millions, thousands, or hundreds.

An advantage for a US phone number is that a programmer knows that it’s ten digits long. To process a ten-digit value, you must discover the 10th digit while ignoring the next nine.

For example, consider the phone number (555) 123-4001 presented in last week’s code. As a value, you must get from 5,551,234,001 to 5,000,000,000. Then you must divide the result by 1,000,000,000 to obtain 5, the first digit in the phone number. This process repeats for the next digit, using 100,000,000 instead of 1,000,000,000 to manipulate the original value.

The following code demonstrates this process. The core of the code is based on the string version presented in last week’s Lesson. The constant values are adjusted to account for the way the digits are processed.

2025_04_05-Lesson.c

#include <stdio.h>
#include <math.h>

int main()
{
    /* phone numbers for this example have ten digits */
    const int digits = 10;
    const int open_paren = 10;
    const int close_paren = 7;
    const int dash = 4;
    long phone,p;
    int x,d;

    /* assign phone number as a long value */
    phone = 5551234001;

    /* process the phone number */
    for( x=digits; x>0; x-- )
    {
        /* extract each digit, left-to-right */
        p = (long)pow(10,x);
        d = (phone % p)/(p/10);

        /* output the formatting phone number */
        switch(x)
        {
            case open_paren:
                printf("(%d",d);
                break;
            case close_paren:
                printf(") %d",d);
                break;
            case dash:
                printf("-%d",d);
                break;
            default:
                printf("%d",d);
        }
    }
    putchar('\n');

    return 0;
}

A for loop works through the phone number’s length, counting from 10 to 1. For each value of digit, its corresponding power of 10 is calculated, (long)pow(10,x), with the result stored in variable p. This step makes the next calculation (the following statement) more compact.

To obtain the digit, the modulus of the phone value and the power of 10 is calculated, then divided by the power of 10: (phone % p)/(p/10). The result is the single digit, stored in variable d.

Yes, it took me time to figure out how to perform the math magic in this code. The part I missed was the p/10.

Once the single digit is obtained, the switch-case structure outputs the phone number in the US phone number format, similar to what was presented in last week’s Code.

Here’s a sample run:

(555) 123-4001

Remember that to build this code in Linux, you must include the math library; use the -lm switch at the command prompt.

I’m certain other approaches are possible to obtain each digit from a value, left-to-right. In fact, it would be fun to work the number from right-to-left and then figure out how to output the values backwards. I may present this code sometime in the future.

6 thoughts on “The Phone Number Value

  1. It became a little long, however provides two solutions and benchmarking.
    Compile with ‘gcc -o test test.c -lm’,
    run with ‘./test ( iterations ) ( alternative phone number )’.
    The TIMEITcp output consists of readtsc difference; performance vs. reference ( smaller is better ); iterations; dummy; expression evaluated; ( comment ) .
    More or less a test if posting formatted code works.

    #include <stdio.h> // reg. e.g. printf,
    #include <math.h> // reg. e.g. pow,
    #include <stdint.h> // reg. e.g. uint_32_t.
    #include <cpuid.h> // reg. e.g. __get_cpuid,
    #include <stdlib.h> // reg. e.g. rand,

    static inline void cpuid_ (int32_t output[4], int32_t functionnumber) {
    __get_cpuid(functionnumber, (uint32_t*)output, (uint32_t*)(output+1), (uint32_t*)(output+2), (uint32_t*)(output+3));
    }

    static inline void serialize () {
    __asm __volatile__ (“cpuid” : : “a”(0) : “ebx”, “ecx”, “edx” ); // serialize
    }

    // read time stamp counter
    static inline uint64_t readtsc() {
    uint32_t lo, hi;
    __asm __volatile__ (“rdtsc” : “=a”(lo), “=d”(hi) : : );
    return lo | (uint64_t)hi << 32;
    }

    // read performance monitor counter
    static inline uint64_t readpmc(int32_t n) {
    uint32_t lo, hi;
    __asm __volatile__ (“rdpmc” : “=a”(lo), “=d”(hi) : “c”(n) : );
    return lo | (uint64_t)hi << 32;
    }

    void warmup( ) // awake CPU from powersave,
    {
    volatile double randomNumber;
    for( int k = 0; k < 50000000; k++ ) {
    randomNumber = sqrt( randomNumber ) + rand() % 100 + 1;
    }
    }

    #define TIMEITcp( expr, N, res, format, comment ) \
    serialize(); \
    start1 = readtsc(); \
    for( int i = 1; i <= N; ++i ) \
    { \
    expr; \
    } \
    serialize(); \
    end1 = readtsc(); \
    printf( “%07ld; %10.03f; %1d; ‘%” #format “; %s; %s \n”, end1 – start1, (double)( end1 – start1 ) / reference, N, res, #expr, comment )

    void example( long phone )
    {
    /* phone numbers for this example have ten digits */
    const int digits = 10;
    const int open_paren = 10;
    const int close_paren = 7;
    const int dash = 4;
    long p;
    int x,d;

    /* process the phone number */
    for( x=digits; x>0; x– )
    {
    /* extract each digit, left-to-right */
    p = (long)pow(10,x);
    d = (phone % p)/(p/10);

    /* output the formatting phone number */
    switch(x)
    {
    case 10:
    printf(“(%d”,d);
    break;
    case 7:
    printf(“) %d”,d);
    break;
    case 4:
    printf(“-%d”,d);
    break;
    default:
    printf(“%d”,d);
    }
    }
    putchar(‘\n’);
    }

    void test( long phone )
    {
    char charbuf_15[15]; // buffer to compile output,
    unsigned int position = *charbuf_15; // position in array,

    position = 14; // array index counts zero based,
    charbuf_15[ position ] = ‘\0’; // ensure string terminated,
    for( int i = 4; i > 0; i– ) { // four digits subscriber number,
    charbuf_15[ –position ] = ( ( phone % 10 ) + ‘0’ ); // character is digit value + ASCII code of zero,
    phone /= 10; // strip from number,
    }
    charbuf_15[ –position ] = ( 45 ); // print the dash,
    for( int i = 3; i > 0; i– ) { // three digits exchange code,
    charbuf_15[ –position ] = ( ( phone % 10 ) + ‘0’ );
    phone /= 10;
    }
    charbuf_15[ –position ] = ( 32 ); // print a space,
    charbuf_15[ –position ] = ( 41 ); // print the “)”,
    for( int i = 3; i > 0; i– ) { // three digits area code,
    charbuf_15[ –position ] = ( ( phone % 10 ) + ‘0’ );
    phone /= 10;
    }
    charbuf_15[ –position ] = ( 40 ); // print the “(“,

    printf( “%s\n”, charbuf_15 ); // printout of result,
    }

    void test_2( long phone )
    {
    char charbuf_15[15]; // buffer to compile output,
    unsigned int position = *charbuf_15; // position in array,
    ldiv_t extract;
    extract.quot = phone,

    position = 14; // array index counts zero based,
    charbuf_15[ position ] = ‘\0’; // ensure string terminated,
    for( int i = 4; i > 0; i– ) { // four digits subscriber number,
    extract = ldiv( extract.quot, 10 );
    charbuf_15[ –position ] = ( ( extract.rem ) + ‘0’ ); // character is digit value + ASCII code of zero,
    }

    charbuf_15[ –position ] = ( 45 ); // print the dash,

    for( int i = 3; i > 0; i– ) { // three digits exchange code,
    extract = ldiv( extract.quot, 10 );
    charbuf_15[ –position ] = ( ( extract.rem ) + ‘0’ ); // character is digit value + ASCII code of zero,
    }

    charbuf_15[ –position ] = ( 32 ); // print a space,
    charbuf_15[ –position ] = ( 41 ); // print the “)”,

    for( int i = 3; i > 0; i– ) { // three digits area code,
    extract = ldiv( extract.quot, 10 );
    charbuf_15[ –position ] = ( ( extract.rem ) + ‘0’ ); // character is digit value + ASCII code of zero,
    }

    charbuf_15[ –position ] = ( 40 ); // print the “(“,

    printf( “%s\n”, charbuf_15 ); // printout of result,
    }

    int main( int argc, char* argv[ ] )
    {
    int count = 4;
    clock_t start1, end1;
    double reference = 1;

    if( argc > 1 ) {
    count = atoi( argv[ 1 ] );
    }

    /* assign phone number as a long value */
    long phone = 5551234001;

    if( argc > 2 ) {
    phone = strtold( argv[ 2 ], NULL );
    }

    warmup(); // awake CPU from powersave,
    {

    example( phone ); // dggokin example,
    TIMEITcp( example( phone ), count, “test”, s, “” ); // performance,

    test( phone ); // one test version,
    TIMEITcp( test( phone ), count, “test”, s, “” ); // performance,

    test_2( phone ); // other test version,
    TIMEITcp( test_2( phone ), count, “test”, s, “” ); // performance,

    return 0;
    }

  2. More or less it failed, if someone can correct the “&lt” vs. “” issues … appreciated.

  3. Nice code. I can see influences from C++, but I admire your approach. Try using the <pre> tags. It’s tough to format code as the WordPress filter likes to block out a lot of HTML.

  4. @newbie-02
    What I like to do, to work around the various WordPress limitations when posting code, is the following:

    * put everything in
      <pre>
      …
      </pre>
    * replace < with &lt;
    * replace > with &gt;
    * replace " with &#34;
    * replace ' with &#39;
    * replace SPACEs at the beginning of lines with
      &#xA0; — No-Break Space (NBSP)

    Unrelated, but I would also recommend using rdtscp instead of rdtsc (the former being a serializing instruction while the latter is not). As both GCC/Clang as well as MSVC offer an intrinsic for this instruction, this can be written without resorting to GCCʼs Inline Assembly Language syntax (which I would avoid, if possible):

    #include <stdio.h>

    #if defined(_MSC_VER)
    #include <intrin.h>
    #else
    #include <x86intrin.h>
    #endif

    int main (void)
    {
      unsigned int tsc_aux;
      unsigned long long t1 = __rdtscp (&tsc_aux);
      printf ("Time Stamp Counter: %llu\n", t1);
      return (0);
    }

    As given, the above code is compilable with GCC, Clang and MSVC.

    (That being said, it would probably be even better to just use clock_gettime(MONOTONIC); when on Linux / QueryPerformanceCounter() to support Windows.)

  5. > Try using the &ltpre&gt tags … I did! … try … , they have been at start and end of the pasted code … still learning.

    code;
    code;
    x &lt y;

  6. @M.Stumpfl: rdtscp vs. rdtsc … if … only if … I’d understand the manuals correctly then rdtscp is only partly serializing, and both commands might like fencing by MFENCE, LFENCE and the like … however it’s above my skills how to do in detail, and an effect hard to see in the jitter on recent systems.
    I’m actually struggling with about 5% scatter ( random calls unjustified more than 30% slow or fast versus an average over multiple runs ) and would like that reduced … do you have a tip?

Leave a Reply