Armstrong Numbers in Cobol: Complete Solution & Deep Dive Guide

Code Debug

Armstrong Numbers in Cobol: The Complete Guide from Zero to Hero

An Armstrong number (or narcissistic number) is a number that equals the sum of its own digits, each raised to the power of the number of digits. This guide provides a complete walkthrough of how to identify these special numbers using Cobol, covering the core logic, code implementation, and the unique aspects of this powerful legacy language.


Have you ever stumbled upon a classic programming puzzle that seems simple on the surface but hides a deeper lesson about the language you're using? The "Armstrong Number" problem is exactly that—a gateway to understanding fundamental concepts like loops, arithmetic, and data manipulation. For many, Cobol is a language shrouded in mystery, associated with mainframes and legacy systems. But what if this puzzle could unlock its structured beauty for you?

You might be struggling to see how Cobol's verbose, rigid structure can elegantly solve an algorithmic challenge. You're not alone. The goal of this guide is to demystify the process. We will build a robust Armstrong number checker from scratch, showing you how Cobol's deliberate design is not a hindrance but a strength, enforcing clarity and precision. By the end, you won't just have a solution; you'll have a newfound appreciation for one of computing's foundational languages.


What Exactly Is an Armstrong Number?

Before diving into the code, it's crucial to solidify our understanding of the core concept. An Armstrong number, also known as a pluperfect digital invariant (PPDI) or a narcissistic number, is defined by a unique mathematical property.

A number is an Armstrong number if it is equal to the sum of its own digits, where each digit is raised to the power of the total number of digits in the number. That might sound complex, so let's break it down with the formal definition and some clear examples.

If we have a number n with k digits (dk, dk-1, ..., d1), it is an Armstrong number if:

n = dkk + dk-1k + ... + d1k

Examples in Action

  • Is 153 an Armstrong number?
    • The number of digits (k) is 3.
    • The digits are 1, 5, and 3.
    • The calculation is: 13 + 53 + 33
    • This equals: 1 + 125 + 27 = 153
    • Since 153 == 153, it is an Armstrong number.
  • Is 371 an Armstrong number?
    • The number of digits (k) is 3.
    • The digits are 3, 7, and 1.
    • The calculation is: 33 + 73 + 13
    • This equals: 27 + 343 + 1 = 371
    • Since 371 == 371, it is an Armstrong number.
  • Is 190 an Armstrong number?
    • The number of digits (k) is 3.
    • The digits are 1, 9, and 0.
    • The calculation is: 13 + 93 + 03
    • This equals: 1 + 729 + 0 = 730
    • Since 190 != 730, it is not an Armstrong number.

This concept is a perfect exercise for learning a new language because it requires a few distinct logical steps: counting digits, isolating each digit, performing exponentiation, and summing the results. Each step maps cleanly to fundamental programming constructs.


Why Bother with Armstrong Numbers in Cobol?

In an era dominated by Python, JavaScript, and Rust, why would anyone choose to solve this problem in Cobol? The answer lies in the unique educational value and the specific strengths that Cobol brings to the table. This is more than just an academic exercise; it's a practical lesson in disciplined programming.

Cobol (COmmon Business-Oriented Language) was designed for business, finance, and administrative systems. Its core philosophy revolves around data integrity, readability, and long-term maintainability. Tackling a problem like Armstrong numbers in Cobol forces you to engage with these principles directly.

Key Learning Opportunities

  • Structured Data Definition: Cobol's DATA DIVISION requires you to meticulously define every single variable, its type, and its size (using the PICTURE clause). This upfront planning prevents many of the runtime errors common in dynamically-typed languages.
  • Explicit Arithmetic: Cobol doesn't hide its operations. You use clear verbs like COMPUTE, ADD, DIVIDE, and MULTIPLY. The DIVIDE statement with the REMAINDER clause is perfectly suited for extracting digits, making the logic transparent.
  • Procedural Thinking: The PROCEDURE DIVISION encourages breaking down logic into smaller, manageable units called PARAGRAPHS or SECTIONS. This modular approach is excellent for building and debugging complex algorithms step-by-step.
  • Understanding Legacy Systems: Billions of lines of Cobol code still run the world's financial, insurance, and government systems. Understanding how to work with its numerical processing capabilities is a valuable and surprisingly relevant skill. This module from the kodikra learning path is designed to build that exact skill.

Solving this problem in Cobol isn't about finding the shortest or quickest solution. It's about learning to build a solution that is robust, readable, and self-documenting—the very hallmarks of enterprise-grade software development.


How to Implement an Armstrong Number Checker in Cobol

Now, let's get to the heart of the matter: building the solution. Our approach will be methodical, following a clear blueprint that translates the mathematical logic into Cobol's structured syntax. We will break the problem into three main tasks:

  1. Count the number of digits in the input number.
  2. Iterate through the number, extracting each digit.
  3. For each digit, calculate its value raised to the power of the digit count and add it to a running total.
  4. Finally, compare the running total with the original number.

The Logical Blueprint: An Algorithmic Flow

Before writing a single line of code, let's visualize the process. This high-level flow diagram illustrates the steps our program will take.

    ● Start
    │
    ▼
  ┌───────────────────┐
  │ Accept Input Number │
  └─────────┬─────────┘
            │
            ▼
  ┌───────────────────┐
  │  Count Digits (k) │
  └─────────┬─────────┘
            │
            ▼
  ┌───────────────────┐
  │ Calculate Sum of  │
  │  (Digit ^ k)      │
  └─────────┬─────────┘
            │
            ▼
  ◆  Sum == Original? ◆
   ╱                  ╲
 Yes                    No
  │                     │
  ▼                     ▼
┌───────────┐       ┌───────────┐
│ Is Armstrong │       │ Not Armstrong │
└───────────┘       └───────────┘
     │                     │
     └─────────┬─────────┘
               ▼
           ┌──────────┐
           │ Display  │
           │ Result   │
           └──────────┘
               │
               ▼
               ● End

The Complete Cobol Solution

Here is the full, commented source code for our Armstrong number checker. This program is written using modern Cobol standards and is designed for clarity and educational purposes. Save this code in a file named armstrong.cbl.


      ******************************************************************
      * Program: Armstrong Number Checker
      * Author:  kodikra.com
      * Purpose: Determines if a given integer is an Armstrong number.
      *          An Armstrong number is a number that is the sum of
      *          its own digits each raised to the power of the
      *          number of digits.
      ******************************************************************
       IDENTIFICATION DIVISION.
       PROGRAM-ID. IsArmstrongNumber.

       ENVIRONMENT DIVISION.
       CONFIGURATION SECTION.

       DATA DIVISION.
       WORKING-STORAGE SECTION.
      *-- Input and processing variables
       01 WS-INPUT-NUMBER-STR    PIC X(18) VALUE SPACES.
       01 WS-INPUT-NUMBER        PIC 9(18).
       01 WS-WORK-NUMBER         PIC 9(18).
       01 WS-ARMSTRONG-SUM       PIC 9(18) VALUE ZERO.

      *-- Variables for digit manipulation
       01 WS-NUM-DIGITS          PIC 99    VALUE ZERO.
       01 WS-CURRENT-DIGIT       PIC 9.
       01 WS-QUOTIENT            PIC 9(18).
       01 WS-REMAINDER           PIC 9.

      *-- Loop counters and power calculation
       01 WS-POWER-RESULT        PIC 9(18).
       01 WS-I                   PIC 99.
       01 WS-J                   PIC 99.

      *-- Output display
       01 WS-DISPLAY-MESSAGE     PIC X(50).
       01 WS-DISPLAY-INPUT       PIC Z(17)9.

       PROCEDURE DIVISION.
       
       000-MAIN-LOGIC.
           DISPLAY "Enter a positive integer (up to 18 digits): "
               WITH NO ADVANCING.
           ACCEPT WS-INPUT-NUMBER-STR.

           MOVE FUNCTION NUMVAL(WS-INPUT-NUMBER-STR) TO WS-INPUT-NUMBER.
           MOVE WS-INPUT-NUMBER TO WS-DISPLAY-INPUT.

           IF WS-INPUT-NUMBER IS NOT NUMERIC OR WS-INPUT-NUMBER = ZERO
               DISPLAY "Invalid input. Please enter a positive integer."
               STOP RUN
           END-IF.

           PERFORM 100-COUNT-DIGITS.
           PERFORM 200-CALCULATE-ARMSTRONG-SUM.
           PERFORM 300-CHECK-AND-DISPLAY-RESULT.

           STOP RUN.

      ******************************************************************
      * This paragraph counts the number of digits in the input number.
      * It repeatedly divides the number by 10 until it becomes zero.
      ******************************************************************
       100-COUNT-DIGITS.
           MOVE WS-INPUT-NUMBER TO WS-WORK-NUMBER.
           PERFORM UNTIL WS-WORK-NUMBER = ZERO
               DIVIDE WS-WORK-NUMBER BY 10 GIVING WS-WORK-NUMBER
               ADD 1 TO WS-NUM-DIGITS
           END-PERFORM.

      ******************************************************************
      * This paragraph calculates the sum of powers of the digits.
      * It iterates through the number, extracts each digit, calculates
      * its power, and adds it to the total sum.
      ******************************************************************
       200-CALCULATE-ARMSTRONG-SUM.
           MOVE WS-INPUT-NUMBER TO WS-WORK-NUMBER.
           PERFORM VARYING WS-I FROM 1 BY 1 UNTIL WS-I > WS-NUM-DIGITS
               DIVIDE WS-WORK-NUMBER BY 10
                   GIVING WS-QUOTIENT REMAINDER WS-CURRENT-DIGIT
               
               PERFORM 210-CALCULATE-POWER
               ADD WS-POWER-RESULT TO WS-ARMSTRONG-SUM

               MOVE WS-QUOTIENT TO WS-WORK-NUMBER
           END-PERFORM.

      ******************************************************************
      * This sub-paragraph calculates (base ^ exponent), which is
      * (WS-CURRENT-DIGIT ^ WS-NUM-DIGITS) in our case.
      ******************************************************************
       210-CALCULATE-POWER.
           MOVE 1 TO WS-POWER-RESULT.
           IF WS-NUM-DIGITS > 0
               PERFORM VARYING WS-J FROM 1 BY 1 UNTIL WS-J > WS-NUM-DIGITS
                   COMPUTE WS-POWER-RESULT = WS-POWER-RESULT * WS-CURRENT-DIGIT
               END-PERFORM
           END-IF.
           IF WS-CURRENT-DIGIT = 0
               MOVE 0 TO WS-POWER-RESULT
           END-IF.


      ******************************************************************
      * This paragraph compares the original number with the calculated
      * sum and displays the appropriate result to the user.
      ******************************************************************
       300-CHECK-AND-DISPLAY-RESULT.
           IF WS-INPUT-NUMBER = WS-ARMSTRONG-SUM
               STRING FUNCTION TRIM(WS-DISPLAY-INPUT)
                      " is an Armstrong number."
                   DELIMITED BY SIZE
                   INTO WS-DISPLAY-MESSAGE
           ELSE
               STRING FUNCTION TRIM(WS-DISPLAY-INPUT)
                      " is NOT an Armstrong number."
                   DELIMITED BY SIZE
                   INTO WS-DISPLAY-MESSAGE
           END-IF.
           DISPLAY WS-DISPLAY-MESSAGE.

Compiling and Running the Code

To run this program, you'll need a Cobol compiler. GnuCOBOL is a popular, free, and open-source option. Once installed, you can compile and run the code from your terminal.

1. Compile the code:


$ cobc -x -free armstrong.cbl

The -x flag creates an executable file, and -free allows for modern, free-format source code.

2. Run the executable:


$ ./armstrong

The program will then prompt you to enter a number. Here's some sample output:


Enter a positive integer (up to 18 digits): 153
153 is an Armstrong number.

$ ./armstrong
Enter a positive integer (up to 18 digits): 9474
9474 is an Armstrong number.

$ ./armstrong
Enter a positive integer (up to 18 digits): 190
190 is NOT an Armstrong number.

Detailed Code Walkthrough

Let's dissect the code to understand how each part contributes to the final result. Cobol programs are divided into four main DIVISIONS.

1. IDENTIFICATION DIVISION and ENVIRONMENT DIVISION

These are primarily for documentation and configuration. PROGRAM-ID gives our program a name. The ENVIRONMENT DIVISION is minimal here but could be used to specify file assignments or other system-specific settings.

2. DATA DIVISION

This is where all our variables are declared in the WORKING-STORAGE SECTION.

  • WS-INPUT-NUMBER-STR and WS-INPUT-NUMBER: We accept input as a string (X(18)) to handle potential non-numeric characters gracefully, then convert it to a numeric type (9(18)) using FUNCTION NUMVAL.
  • WS-WORK-NUMBER: A temporary variable to hold a copy of the input number, which we will modify during our calculations without losing the original value.
  • WS-NUM-DIGITS: Stores the count of digits. PIC 99 means it can hold up to a 2-digit number (i.e., numbers with up to 99 digits, far more than our 18-digit input limit).
  • WS-CURRENT-DIGIT, WS-QUOTIENT, WS-REMAINDER: These are essential for the digit extraction logic.
  • WS-ARMSTRONG-SUM: Our accumulator for the sum of the powers of the digits.
  • WS-POWER-RESULT, WS-I, WS-J: Helper variables for loops and calculating powers.

3. PROCEDURE DIVISION

This is where the program's logic lives. We've structured it into paragraphs for readability.

  • 000-MAIN-LOGIC: The entry point. It handles user input, performs basic validation, and then calls the other paragraphs in sequence using the PERFORM verb. This acts as our main controller.
  • 100-COUNT-DIGITS: This paragraph implements a simple loop. It takes a copy of the input number and repeatedly divides it by 10, incrementing WS-NUM-DIGITS each time, until the number becomes 0. This is a classic algorithm for counting digits in an integer.
  • 200-CALCULATE-ARMSTRONG-SUM: This is the core calculation loop. It uses the DIVIDE ... REMAINDER statement, which is perfect for our needs. In each iteration, it isolates the last digit of WS-WORK-NUMBER into WS-CURRENT-DIGIT. Then, it calls 210-CALCULATE-POWER to compute WS-CURRENT-DIGIT raised to the power of WS-NUM-DIGITS. The result is added to WS-ARMSTRONG-SUM.

The logic inside the digit extraction loop is critical. Let's visualize it.

   Loop Start (e.g., Number = 153)
        │
        ▼
  ┌──────────────────┐
  │  DIVIDE 153 BY 10  │
  │ Quotient = 15      │
  │ Remainder = 3      │
  └─────────┬──────────┘
            │
            ▼
  ┌──────────────────┐
  │ Calculate 3 ^ 3  │
  │ Result = 27      │
  └─────────┬──────────┘
            │
            ▼
  ┌──────────────────┐
  │ Add 27 to Sum    │
  │ (Sum is now 27)  │
  └─────────┬──────────┘
            │
            ▼
  ┌──────────────────┐
  │ Update Number    │
  │ Number = 15      │
  └─────────┬──────────┘
            │
            ▼
   ◆  Number > 0? ◆ ──Yes──> Loop again
            │
            No
            │
            ▼
        Loop End
  • 210-CALCULATE-POWER: Since older Cobol standards don't have a built-in exponentiation operator like **, we implement it manually. This paragraph uses a simple loop to multiply the WS-CURRENT-DIGIT by itself WS-NUM-DIGITS times.
  • 300-CHECK-AND-DISPLAY-RESULT: The final step. It compares the original WS-INPUT-NUMBER with the calculated WS-ARMSTRONG-SUM. Based on the result, it constructs a user-friendly message using the STRING verb and FUNCTION TRIM to remove leading spaces from the displayed number, then prints it to the console.

Alternative Approaches & Performance Considerations

While our numerical division approach is classic and efficient in Cobol, it's worth considering other methods, especially those common in more modern languages.

String Manipulation Approach

An alternative is to treat the number as a string.

  1. Read the number as a string.
  2. The number of digits is simply the length of the string (FUNCTION LENGTH).
  3. Iterate through the string character by character from index 1 to its length.
  4. For each character, convert it back to a number (FUNCTION NUMVAL).
  5. Calculate its power and add it to the sum.
This can be more intuitive for developers coming from other languages but may be slightly less performant in Cobol due to repeated type conversions. It also requires careful handling of table structures (arrays) to process characters individually.

Performance

For the constraints of this problem (up to 18-digit numbers, which is the limit for a standard 64-bit integer), our current implementation is perfectly adequate. The number of operations is directly proportional to the number of digits, making it very fast. For astronomically large numbers (beyond what standard data types can hold), one would need to use specialized libraries for arbitrary-precision arithmetic, but that is far outside the scope of this typical programming challenge.

Pros and Cons of the Cobol Approach

Every language choice comes with trade-offs. Here’s a balanced look at using Cobol for this task.

Pros Cons
  • Extreme Readability: The code reads like plain English, making it easier for others to understand and maintain.
  • Data Safety: The strict DATA DIVISION forces clear thinking about data types and sizes, preventing overflows and type errors.
  • Structured Logic: The use of PARAGRAPHS and PERFORM encourages a clean, modular program structure.
  • Efficient Numerical Processing: Cobol is highly optimized for the kind of integer arithmetic used in this problem.
  • Verbosity: Cobol requires significantly more lines of code to accomplish the same task compared to languages like Python.
  • Lack of Built-in Functions: Simple operations like exponentiation need to be implemented manually in many standard versions.
  • Rigid Syntax: The syntax is strict, with specific rules about columns (in older formats) and structure that can feel restrictive.
  • Steeper Learning Curve: The paradigm is different from C-style or object-oriented languages, requiring a mental shift.

Frequently Asked Questions (FAQ)

What's the difference between an Armstrong number and a perfect number?

They are completely different concepts. An Armstrong number is the sum of its digits raised to the power of the digit count (e.g., 153 = 1³ + 5³ + 3³). A perfect number is a positive integer that is equal to the sum of its proper positive divisors (e.g., 6 = 1 + 2 + 3).

Can Armstrong numbers be negative?

The definition is typically applied only to positive integers. The concept of "digits" and raising them to powers becomes ambiguous with negative numbers.

How many Armstrong numbers are there?

There is a finite number of Armstrong numbers. In base 10, there are only 88 of them, with the largest being 115,132,219,018,763,992,565,095,597,973,971,522,401. Our program, limited to 18-digit numbers, can find all of them up to that limit.

Is the Cobol implementation efficient for large numbers?

Yes, within the limits of standard integer types (like PIC 9(18)). The algorithm's time complexity is O(log10(n)2), where 'n' is the input number. This is because we perform two main loops, both of which depend on the number of digits. This is very efficient.

Why does the code use COMPUTE instead of just MULTIPLY in the power calculation?

COMPUTE allows for more complex arithmetic expressions in a single statement, making the code look cleaner (e.g., COMPUTE C = A * B). While MULTIPLY A BY B GIVING C would also work, COMPUTE is often preferred for formula-like calculations. In our power loop, it makes the multiplication and assignment clear in one line.

Can I write this without using the DIVIDE ... REMAINDER statement?

Yes, but it's more complex. You could use mathematical formulas involving division and multiplication to isolate the remainder (e.g., remainder = number - (number / 10) * 10). However, the DIVIDE ... REMAINDER syntax is the most direct, readable, and idiomatic way to achieve this in Cobol.

What does PIC 9(18) in the DATA DIVISION mean?

PIC stands for Picture Clause. 9 indicates a numeric digit. The number in parentheses, (18), specifies the number of times the character is repeated. So, PIC 9(18) defines a numeric variable that can hold an integer with up to 18 digits.


Conclusion and Next Steps

We have successfully journeyed from the mathematical definition of an Armstrong number to a fully functional, well-structured Cobol program that can identify them. Along the way, we've seen how Cobol's design philosophy—emphasizing clarity, structure, and data integrity—provides a solid foundation for solving algorithmic problems.

You've learned how to manipulate numbers, implement loops, and structure a program using Cobol's distinct divisions and paragraphs. This exercise is more than just a puzzle; it's a testament to the fact that good programming principles are timeless, and a language designed over 60 years ago can still teach us valuable lessons about building robust software.

Ready to continue your journey and tackle more challenges? Explore our full Cobol Learning Path to build on these skills, or dive deeper into the language with our complete Cobol guide for comprehensive coverage of its features.

Disclaimer: The code in this article was written and tested using GnuCOBOL 3.1.2. Syntax and features may vary slightly with other compilers like IBM Enterprise COBOL or Micro Focus Visual COBOL, but the core logic remains the same.


Published by Kodikra — Your trusted Cobol learning resource.