All Gems »

Filter by Products
Next Next

# Gem #7: The Beauty of Numeric Literals in Ada

### Let's get started…

On an unusually hot end-of-summer day some years ago, I stepped into the Courant Institute of Mathematical Sciences. It was my first day of class at New York University and my first day with Ada. The heat was pounding and the classroom air-conditioning unit was being repaired.

The breeze that day came from something simple and elegant: numeric literals in Ada. I was fortunate that my programming languages class started with something so cool. The first thing that struck me is the ability to use underscores to separate groups of digits. I always found

```  3.14159_26535_89793_23846_26433_83279_50288_41971_69399_37510
```

more readable and less error prone to type than

```     3.14159265358979323846264338327950288419716939937510
```

what do you think? (I wish underscores were allowed when typing one's credit card number on the internet.)

This is just a cool beginning. Let's talk about based literals. I never understood the rationale behind the strange and unintuitive method to specify based literals in the C programming language where you have only 3 possible bases: 8, 10, and 16 (why no base 2?). Furthermore, requiring that numbers in base 8 be preceded by a zero feels like a bad joke on us programmers (what values do 0210 and 210 represent in C?)

To my pleasant surprise that humid end-of-summer day, I learnt that Ada allows any base from 2 to 16 and that we can write the decimal number 136 in any one of the following notations

```        2#1000_1000#     8#210#     10#136#     16#88#
```

Coming from a microcontroller background where my I/O devices were memory mapped I liked the ability to write:

```        Lights_On  : constant := 2#1000_1000#;
Lights_Off : constant := 2#0111_0111#;
```

and have the ability to turn on/off the lights as follows:

```   Output_Devices := Output_Devices  or   Lights_On;
Output_Devices := Output_Devices  and  Lights_Off;
```

Of course we can also use records with representation clauses to do the above, which is even more elegant, and I leave that to a future gem (any volunteers out there?).

Back to based literals. The notion of base in Ada allows for exponents. That is particularly pleasant. For instance we can write:

```     Kilobinary  : constant := 2#1#e+10;
Megabinary  : constant := 2#1#e+20;
Gigabinary  : constant := 2#1#e+30;
Terabinary  : constant := 2#1#e+40;
Petabinary  : constant := 2#1#e+50;
Exabinary   : constant := 2#1#e+60;
Zettabinary : constant := 2#1#e+70;
Yottabinary : constant := 2#1#e+80;
```

In based literals the exponent, like the base, uses the regular decimal notation and specifies the power of the base that the based literal should be multiplied with to obtain the final value. For instance `2#1#e+10 = 1 x 210 = 1_024` (in base 10), whereas `16#F#e+2 = 15 x 162= 15 x 256 = 3_840` (in base 10).

Based numbers apply equally well to real literals. We can for instance write:

```    One_Third : constant := 3#0.1#;  --  same as 1.0/3
```

Whether we write `3#0.1#` or `1.0/3`, or even `3#1.0#e-1`, Ada allows us to specify exactly rational numbers for which decimal literals cannot be written.

This brings us to the last nice feature of Ada for this gem. As Bob Duff would put it: Ada has an open-ended set of integer and real types.

As a result, numeric literals in Ada do not carry with them their type as in C. The actual type of the literal is determined from the context. This is particularly helpful in avoiding overflows, underflows, and loss of precision (think about 32l in C, which is very different from 321).

And this is not all: all constant computations done at compile time are done in infinite precision be they integer or real. This allows us to write constants with whatever size and precision without having to worry about overflow or underflow. We can for instance write:

```           Zero : constant := 1.0 - 3.0 * One_Third;
```

and be guaranteed that constant Zero has indeed value zero. This is very different from writing:

```One_Third_Approx : constant := 0.33333333333333333333333333333;
Zero_Approx      : constant := 1.0 - 3.0 * One_Third_Approx;
```

where `Zero_Approx` is really `1.0e-29` (and that will show up in your numerical computations.) The above is quite handy when we want to write fractions without any loss of precision. Along these same lines we can write:

```   Big_Sum : constant := 1           +
Kilobinary  +
Megabinary  +
Gigabinary  +
Terabinary  +
Petabinary  +
Exabinary   +
Zettabinary;

Result : constant := (Yottabinary - 1) / (Kilobinary - 1);
Nil : constant := Result - Big_Sum;
```

and be guaranteed that Nil is equal to zero.

But I am getting carried away by the elegance of Ada numeric literals and almost forgot about the date of our next gem which will be on the `2#10_10#` of September (sorry I couldn't resist :) .

Have a happy summer fellow developers.

### Related Source Code

Ada Gems example files are distributed by AdaCore and may be used or modified for any purpose without restrictions.

#### Attached Files

Franco Gasperoni

Franco Gasperoni is co-founder and Managing Director of AdaCore in Europe. He has been involved with Ada both commercially and technically since 1991. Franco has an engineering degree from the Ecole des Mines de Paris, France and a PhD in Computer Science from New York University, USA. While at the Ecole des Mines, Franco worked with Maurice Allais, the French Economics Nobel laureate. Franco has lectured and conducted research at New York University and at the Ecole des Telecommunications (ENST), in Paris. Franco has published over 25 papers.

• Christoph Grein
Jun 28th, 2007

Franco,

today, on the day of the Lord, the 7#40.6.5565#, I have to tell you the sad truth that your numbers like Kilobyte are wrong.

Kilobyte = 1 kB = 10#1#e3 B, always,

see .

What you mean is Kibibyte = 1 KiB = 2#1#e10 B.

Please note the capitalisation: k kilo, Ki kilobinary.

It really makes a difference if you have 1 GiB or 1 GB. The difference amounts to 7#1_553_536_512# B.

Christoph ;-)

• Christoph Grein
Jun 28th, 2007

because I encluded it in “smaller” - “greater” brackets. Please see there for binary and decimal prefixes.

• Franco Gasperoni
Jun 28th, 2007

When I learnt all of this - in the late 70s - a kilobyte was 1_024 bytes. I found this kind of cute at the time and also kind of confusing.

Thank you Christoph for bringing me up to date on this.

I wonder whether it is common practice these days
to talk about kilobinary bytes (or kibibytes :)

By the way for everyone’s benefit on page:

NIST writes:

Because the SI prefixes strictly represent powers
of 10, they should not be used to represent powers
of 2. Thus, one kilobit, or 1 kbit, is 1_000 bit and
not 210 bit = 1_024 bit. To alleviate this ambiguity,
prefixes for binary multiples have been adopted by
the International Electrotechnical Commission (IEC)
for use in information technology.

See

http://physics.nist.gov/cuu/Units/binary.html

for the details.

I have changed the variable names so now instead of Kilobyte I use Kilobinary :)

• Christoph Grein
Jun 29th, 2007

“When I learnt all of this - in the late 70s - a kilobyte was 1_024 bytes. I found this kind of cute at the time and also kind of confusing.
...
I wonder whether it is common practice these days
to talk about kilobinary bytes (or kibibytes :)”

No, I haven’t ever heard someone use these prefixes, which is kind of sad because of the confusion you mention.

So I begin to spread the (old) news :-)

• Matthew Heaney
Jun 29th, 2007

The convention we use in the US is:

kb = 1000
Kb = 1024

mb = 1000^2
Mb = 1024^2

etc

• Christoph Grein
Jun 29th, 2007

Not so good if you consider that capitalisation is generally looked upon as something optional.

4 mS is Millisiemens, not milliseconds (which is meant and I see often).

And on some devices, there are (still) no lower case characters.

So what would 10 MB mean?

• Francisco J. Montoya
Aug 1st, 2007

Hello, Franco.

I’m not sure whether I understood correctly that of “infinite precision” in compilation-time computations related to literals.

In the line:

Zero : constant := 1.0 - 3.0 * One_Third;

, Zero is guaranteed to be 0.0 due only to the nature of the right-side operands of “:=”, or to the nature of the left-side argument (because Zero is a named number, and not a typed constant), or for both reasons?

I mean, would it be 0.0 in the same way if Zero were a floating-point typed constant, instead of a named number?

Thank you for your gem :-)

Francisco J. Montoya

• Ville Witt
Sep 4th, 2007

Dear Mr. Francisco J. Montoya:
Zero is guaranteed to be 0, because 3.0 * One_Third is 1 exactly. This is to show that One_Third is really 1/3, and not (with finite decimals of 3) 0.333

What is 0.333 + 0.333 + 0.333 ? It’s of cause 0.999.
But if you mean 1/3 + 1/3 + 1/3 you suspect the result to be 1. Ada let you stop wondering about the amounts of bit of the target architecture, and instead explain to the compile what you really mean.

Sincerly Ville Witt (.net)

P.S.: I’m sorry if I was wrong about the above - I myself is quite new to Ada and is fascinated by it. We need people to update their Ada-on-Linux tutorials!

Comments on Gems are now closed.