Image of a smashed clock with a dark brown face and broken white hands and numbers.

PHP INTL In-Depth: Convert Numbers to Roman Numerals

In this PHP INTL In-Depth look, you will understand how to convert integer numbers to Roman numerals (1 => I, 2 => II, 3 => III, etc). There are a few different functions floating around the web on Stack Overflow and blogs. They use an array of values to convert numbers to Roman numerals, but are not needed. Before we get to the code, we need to know what these numerals are:

Roman Numerals? What are those?

So what exactly are Roman numerals? They are a numeral system: a number set represented symbols. Not to be confused with number systems, which are a set of specific numbers. A number system is a more abstract idea of a set of numbers, such as real or rational numbers, while a numeral system is how a set of numbers are expressed, such as decimals or binary in computer hardware.

Roman numerals first appeared in ancient Rome. Europe continued to use until the Middle Ages after the Roman Empire fell. Afterward, they were replaced by the Arabic numerals or Latin digits (1, 2, 3, etc) used throughout much of the world today.

Roman numerals are still used today but are an artistic choice. Examples of where they are still used are circular analog clock faces, company marketing or advertising campaigns, and buildings or monuments with engravings of years.

Roman numerals are read as a basic mathematical expression. Smaller numerals positioned before a larger numeral are subtracted and numbers after are added. For instance, IIV represents the number 3 where V is equal to 5 and I is equal to 1, but since they are placed before the 5 they are subtracted. Numbers of the same or lesser value positioned after a larger number are added. For instance, MMXXIII which represents the year 2023.

INTL: NumberFormatter Decimal Numbering System

PHP’s INTL (internationalization or i8n) library comes with a number formatter class meant to format integers & floats into human-readable numbers, currencies, and accounting. However, there is also a numbering system built into it capable of a few different features. One of those features is Roman numerals.

Instead of creating a function or method to convert numbers to numerals, there is a C/C++ library already compiled and wrapped by PHP: The INTL library. Within that library is a NumberFormatter class which provides access to the powerful ICU Unicode internationalization formatting class.

The formatting class is very powerful and can be used to format a number in various ways. An input is always just a plain, bland, number such as 645002. However, humans have trouble understanding large groups of numbers without something to indicate how many positions there are.

The NumberFormatter can be used to convert that number to a more understandable 645,002. In other countries a dot may be used as the separator and formatted as 645.002. It could also be used to spell out the number as six-hundred forty-five thousand two.

In this case, we want the representation to be a Roman numeral. So we can tell the formatter to use Roman numerals by appending to or replacing the locale with a keyword:

function intToRomanNumeral(int $num) {
    static $nf = new NumberFormatter('@numbers=roman', NumberFormatter::DECIMAL);
    return $nf->format($num);

Here are some output examples:

echo intToRomanNumeral(2); // II

echo intToRomanNumeral(5); // V

echo intToRomanNumeral(10); // X

echo intToRomanNumeral(50); // L

echo intToRomanNumeral(57); // LVII
echo intToRomanNumeral(58); // LVIII

echo intToRomanNumeral(100); // C

echo intToRomanNumeral(150); // CL

echo intToRomanNumeral(1000); // M

echo intToRomanNumeral(10000); // ↂ

The formatter will also format zero as N and negative numbers the same as positive numbers but preceded by a Unicode minus symbol (not the hyphen/minus symbol on keyboards). There are technically no actual zero or negative Roman numerals. They didn’t exist back when the numeral system was developed so the inclusion of them is a modern representation.

You can alternatively use the MessageFormatter class in the same way:

function intToRomanNumeral(int $num) {
    static $nf = new MessageFormatter('@numbers=roman', '{0, number}');
    return $nf->format([$num]);

There is a caveat: performance. In my own testing, this function is about 6 times slower than NumberFormatter. You can use it for individual formats within a string, but if you are planning on using it in a loop or with many messages, it will be more performant use NumberFormatter by itself or pass a numeral converted by NumberFormatter to the MessageFormatter arguments.

I also posted these code examples on GitLab Snippets and Stack Overflow.


Leave a Reply

Your email address will not be published. Required fields are marked *