# Numerics

## Modular Types

In the Introduction to Ada course, we've seen that Ada has two kinds of integer type: signed and modular. For example:

In this section, we discuss two attributes of modular types: `Modulus`

and `Mod`

. We also discuss operations on modular types.

In the Ada Reference Manual

`Modulus`

Attribute

The `Modulus`

attribute returns the modulus of the modular type as a
universal integer value. Let's get the modulus of the 32-bit `Modular`

type that we've declared in the `Num_Types`

package of the previous
example:

When we run this example, we get 4294967296, which is equal to `2**32`

.

`Mod`

Attribute

Note

This section was originally written by Robert A. Duff and published as Gem #26: The Mod Attribute.

Operations on signed integers can overflow: if the result is outside the base
range, `Constraint_Error`

will be raised. In our previous example, we
declared the `Signed_Integer`

type:

```
type Signed_Integer is range 1 .. 1_000_000;
```

The base range of `Signed_Integer`

is the range of
`Signed_Integer'Base`

, which is chosen by the compiler, but is likely to
be something like `-2**31 .. 2**31 - 1`

. (Note: we discussed the
`Base`

attribute in this section.)

Operations on modular integers use modular (wraparound) arithmetic. For example:

Negating X gives -1, which wraps around to `2**32 - 1`

, i.e.
all-one-bits.

But what about a type conversion from signed to modular? Is that a signed
operation (so it should overflow) or is it a modular operation (so it should
wrap around)? The answer in Ada is the former — that is, if you try to
convert, say, `Integer'(-1)`

to `Modular`

, you will get
`Constraint_Error`

:

To solve this problem, we can use the `Mod`

attribute:

The `Mod`

attribute will correctly convert from any integer type to a
given modular type, using wraparound semantics.

Historically

In older versions of Ada — such as Ada 95 —, the only way to do
this conversion is to use `Unchecked_Conversion`

, which is somewhat
uncomfortable. Furthermore, if you're trying to convert to a generic formal
modular type, how do you know what size of signed integer type to use? Note
that `Unchecked_Conversion`

might malfunction if the source and target
types are of different sizes.

The `Mod`

attribute was added to Ada 2005 to solve this problem.
Also, we can now safely use this attribute in generics. For example:

In this example, `F`

will return the all-ones bit pattern, for
whatever modular type is passed to `Formal_Modular`

.

### Operations on modular types

Modular types are particularly useful for bit manipulation. For example, we
can use the `and`

, `or`

, `xor`

and `not`

operators for
modular types.

Also, we can perform bit-shifting by multiplying or dividing a modular object
with a power of two. For example, if `M`

is a variable of modular type,
then `M := M * 2 ** 3;`

shifts the bits to the left by three bits.
Likewise, `M := M / 2 ** 3`

shifts the bits to the right. Note that the
compiler selects the appropriate shifting operator when translating these
operations to machine code — no actual multiplication or division will be
performed.

Let's see a simple implementation of the CRC-CCITT (0x1D0F) algorithm:

In this example, the core of the algorithm is implemented in the
`Crc_CCITT`

function. There, we use bit shifting — for instance,
`* 2 ** 8`

and `/ 2 ** 8`

, which shift left and right, respectively,
by eight bits. We also use the `xor`

operator.

## Numeric Literals

### Classification

We've already discussed basic characteristics of numeric literals in the Introduction to Ada course. We've seen that there are two kinds of numeric literals in Ada: integer literals and real literals. They are distinguished by the absence or presence of a radix point. For example:

Another classification takes the use of a base indicator into account.
(Remember that, when writing a literal such as `2#1011#`

, the base is the
element before the first `#`

sign.) So here we distinguish between decimal
literals and based literals. For example:

Based literals use the `base#number#`

format. Also, they aren't limited to
simple integer literals such as `16#16D#`

. In fact, we can use a radix
point or an exponent in based literals, as well as underscores. In addition, we
can use any base from 2 up to 16. We discuss these aspects further in the next
section.

### Features and Flexibility

Note

This section was originally written by Franco Gasperoni and published as Gem #7: The Beauty of Numeric Literals in Ada.

Ada provides a simple and elegant way of expressing numeric literals. One of
those simple, yet powerful aspects is the ability to use underscores to
separate groups of digits. For example,
`3.14159_26535_89793_23846_26433_83279_50288_41971_69399_37510`

is more
readable and less error prone to type than
`3.14159265358979323846264338327950288419716939937510`

. Here's the
complete code:

Also, when using based literals, Ada allows any base from 2 to 16. Thus, we can write the decimal number 136 in any one of the following notations:

In other languages

The rationale behind the method to specify based literals in the C
programming language is strange and unintuitive. Here, you have only three
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. For example, what values do `0210`

and `210`

represent
in C?

When dealing with microcontrollers, we might encounter I/O devices that are memory mapped. Here, we have 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;
```

Here's the complete example:

Of course, we can also use records with representation clauses to do the above, which is even more elegant.

The notion of base in Ada allows for exponents, which is particularly pleasant. For instance, we can write:

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 2^{10} = `1_024`

(in base 10), whereas
`16#F#e+2`

= 15 x 16^{2} = 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.

The last nice feature is that 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, for example, 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.

In other languages

In C, a source of confusion can be the distinction between `32l`

and
`321`

. Although both look similar, they're actually very different from
each other.

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. Here's the complete code:

Along these same lines, we can write:

and be guaranteed that `Nil`

is equal to zero.

## Floating-Point Types

In this section, we discuss various attributes related to floating-point types.

In the Ada Reference Manual

### Representation-oriented attributes

#### Attribute: `'Machine_Radix`

`'Machine_Radix`

is an attribute that returns the radix of the hardware
representation of a type. For example:

Usually, this value is two, as the radix is based on a binary system.

#### Attributes: `'Machine_Mantissa`

, `'Machine_Emin`

and `Machine_Emax`

`'Machine_Mantissa`

is an attribute that returns the number of bits
reserved for the mantissa of the floating-point type. The `Machine_Emin`

and `Machine_Emax`

attributes return the minimum and maximum value,
respectively, of the machine exponent the floating-point type. Note that, in
all cases, the returned value is a universal integer. For example:

On a typical desktop PC, as indicated by `'Machine_Mantissa`

, we have 24
bits for the floating-point mantissa of the `Float`

type.

To get the actual minimum and maximum value of the exponent for a specific
type, we need to use `'Machine_Radix`

that we've just discussed in the
previous section. Let's calculate the minimum and maximum value of the exponent
for the `Float`

type on a typical PC:

Minimum exponent:

`Float'Machine_Radix ** Float'Machine_Emin`

.In our target platform, this is 2

^{-125}= 2.35098870164457501594 x 10^{-38}.

Maximum exponent:

`Float'Machine_Radix ** Float'Machine_Emax`

. In thisIn our target platform, this is 2

^{128}= 3.40282366920938463463 x 10^{38}.

#### Attribute: `'Digits`

`'Digits`

is an attribute that returns the requested decimal precision of
a floating-point subtype. Let's see an example:

On a typical desktop PC, the requested decimal precision of the `Float`

type is six digits.

Note that we said that `Digits`

is the *requested* level of precision,
which is specified as part of declaring a floating point type. We can retrieve
the actual decimal precision with `'Base'Digits`

. For example:

On a typical desktop PC, the requested decimal precision of the `Float_D3`

type is three digits, while the actual decimal precision is six digits.

#### Attributes: `'Denorm`

, `Signed_Zeros`

, `'Machine_Rounds`

, `Machine_Overflows`

In this section, we discuss attributes that return `Boolean`

values
indicating whether a feature is available or not in the target architecture:

`'Denorm`

is an attribute that indicates whether the target architecture uses denormalized numbers.`'Signed_Zeros`

is an attribute that indicates whether the type uses a sign for zero values, so it can represent both -0.0 and 0.0.`'Machine_Rounds`

is an attribute that indicates whether rounding-to-nearest is used, rather than some other choice (such as rounding-toward-zero).`Machine_Overflows`

is an attribute that indicates whether a`Constraint_Error`

is (or is not) guaranteed to be raised when an operation with that type produces an overflow or divide-by-zero.

On a typical PC, `'Denorm`

, `'Signed_Zeros`

, `'Machine_Rounds`

are true, while `'Machine_Overflows`

is false.

### Primitive function attributes

#### Attributes: `'Fraction`

, `'Exponent`

and `Compose`

`'Exponent`

is an attribute that returns the machine exponent of a
floating-point value, while `'Fraction`

is an attribute that returns the
mantissa part of a floating-point value. `'Compose`

is used to return a
floating-point value based on a fraction (the mantissa part) and the machine
exponent. For example:

For example, considering that `Float'Machine_Radix`

is two, we see that
the value 1.0 is composed by a fraction of 0.5 and a machine exponent of one.
In other words, 0.5 x 2^{1} = 1.0. For the value 0.25, we get a fraction
of 0.5 and a machine exponent of -1, which makes 0.5 x 2^{-1} = 0.25.
We can use the `'Compose`

attribute to perform this calculation. For
example, `Float'Compose (0.5, -1) = 0.25`

.

Note that `Fraction`

is always between 0.5 and 0.999999 (i.e < 1.0),
except for denormalized numbers, where it can be < 0.5.

#### Attribute: `'Scaling`

`'Scaling`

is an attribute that scales a floating-point value based on the
machine radix and a machine exponent passed to the function. For example:

This is calculated with this formula: value x Machine_Radix^{machine exponent}. For example, on a typical PC with a machine radix of
two, `Float'Scaling (0.25, 3)`

corresponds to 0.25 x 2^{3} = 2.0.

#### Attributes: `'Floor`

, `Ceiling`

`'Floor`

and `'Ceiling`

are attributes that returned the rounded-down
or rounded-up value, respectively, of a floating-point value. For example:

As we can see in this example, the rounded-down value (floor) of 0.25 is 0.0, while the rounded-up value (ceiling) of 0.25 is 1.0.

#### Attributes: `'Rounding`

, `Unbiased_Rounding`

, `Machine_Rounding`

In this section, we discuss three attributes used for rounding. In all cases, the rounding attributes return the nearest integer value (as a floating-point value). For example, the rounded value for 4.8 is 5.0 because 5 is the closest integer value.

Let's see some examples:

The difference between these attributes is the way they handle the case when a value is exactly in between two integer values. For example, 4.5 could be rounded up to 5.0 or rounded down to 4.0. This is the way each rounding attribute works in this case:

`'Rounding`

rounds away from zero. Positive floating-point values are rounded up, while negative floating-point values are rounded down when the value is between two integer values. For example:4.5 is rounded-up to 5.0, i.e.

`Float'Rounding (4.5) = Float'Ceiling (4.5) = 5.0`

.-4.5 is rounded-down to -5.0, i.e.

`Float'Rounding (-4.5) = Float'Floor (-4.5) = -5.0`

.

`Unbiased_Rounding`

rounds toward the even integer. For example,`Float'Unbiased_Rounding (0.5) = 0.0`

because zero is the closest even integer, while`Float'Unbiased_Rounding (1.5) = 2.0`

because two is the closest even integer.

`Machine_Rounding`

uses the most appropriate rounding instruction available on the target platform. While this rounding attribute can potentially have the best performance, its result may be non-portable. For example, whether the rounding of 4.5 becomes 4.0 or 5.0 depends on the target platform.If an algorithm depends on a specific rounding behavior, it's best to avoid the

`Machine_Rounding`

attribute. On the other hand, if the rounding behavior won't have a significant impact on the results, we can safely use this attribute.

#### Attributes: `'Truncation`

, `Remainder`

, `Adjacent`

The `'Truncation`

attribute returns the *truncated* value of a
floating-point value, i.e. the value corresponding to the integer part of a
number rounded toward zero. This corresponds to the number before the radix
point. For example, the truncation of 1.55 is 1.0 because the integer part of
1.55 is 1.

The `'Remainder`

attribute returns the remainder part of a division. For
example, `Float'Remainder (1.25, 0.5) = 0.25`

. Let's briefly discuss the
details of this operations. The result of the division 1.25 / 0.5 is 2.5. Here,
1.25 is the dividend and 0.5 is the divisor. The quotient and remainder of this
division are 2 and 0.25, respectively. Here, the quotient is an integer number,
and the remainder is the floating-point part that remains. Note that the
relation between quotient and remainder is defined in such a way that we get
the original dividend back when we use the formula: "quotient x divisor +
remainder = dividend". For the previous example, this means 2 x 0.5 + 0.25
= 1.25.

The `Adjacent`

attribute is the next machine value towards another value.
For example, on a typical PC, the adjacent value of a small value —
say, 1.0 x 10^{-83} — towards zero is +0.0, while the adjacent
value of this small value towards 1.0 is another small, but greater value
— in fact, it's 1.40130 x 10^{-45}. Note that the first parameter
of the `Adjacent`

attribute is the value we want to analyze and the
second parameter is the `Towards`

value.

Let's see a code example:

#### Attributes: `'Copy_Sign`

and `Leading_Part`

`'Copy_Sign`

is an attribute that returns a value where the sign of the
second floating-point argument is multiplied by the magnitude of the first
floating-point argument. For example, `Float'Copy_Sign (1.0, -10.0)`

is
-1.0. Here, the sign of the second argument (-10.0) is multiplied by the
magnitude of the first argument (1.0), so the result is -1.0.

`'Leading_Part`

is an attribute that returns the *approximated* version of
the mantissa of a value based on the specified number of leading bits for the
mantissa. For example, `Float'Leading_Part (3.1416, 1)`

is 2.0 because
that's the value we can represent with one leading bit. (Note that
`Float'Fraction (2.0) = 0.5`

— which can be represented with one
leading bit in the mantissa — and `Float'Exponent (2.0) = 2`

.) If we
increase the number of leading bits of the mantissa to two — by writing
`Float'Leading_Part (3.1416, 2)`

—, we get 3.0 because that's the
value we can represent with two leading bits. If we increase again the number
of leading bits to five — `Float'Leading_Part (3.1416, 5)`

—,
we get 3.125. Note that, in this case `Float'Fraction (3.125) = 0.78125`

and `Float'Exponent (3.125) = 2`

. The binary mantissa is actually
`2#110_0100_0000_0000_0000_0000#`

, which can be represented with five
leading bits as expected: `2#110_01#`

. (Note that we can get the mantissa
by calculating
`Float'Fraction (3.125) * Float (Float'Machine_Radix) ** (Float'Machine_Mantissa - 1)`

and converting the result to binary format. The -1 value in the formula
corresponds to the sign bit.)

Attention

In this explanation about the `'Leading_Part`

attribute, we're
talking about leading bits. Strictly speaking, however, this is actually a
simplification, and it's only correct if `Machine_Radix`

is equal to
two — which is the case for most machines. Therefore, in most cases,
the explanation above is perfectly acceptable.

However, if `Machine_Radix`

is *not* equal to two, we cannot use the
term "bits" anymore, but rather digits of the `Machine_Radix`

.

Let's see some examples:

Todo

Add discussion about `Machine`

.

```
with Ada.Text_IO; use Ada.Text_IO;
procedure Show_Copy_Sign_Leading_Part_Machine is
begin
-- NOTE: no clear usage for 'Machine!!
Put_Line (Float'(1.000015)'Image);
Put_Line (Float'Machine (1.000015)'Image);
Put_Line (Float'Image (Float'Machine (1.000015) - Float'(1.0000)));
Put_Line (Float'Image (Float'(1.000015) - Float'(1.0000)));
Put_Line (Float'Fraction (Float'Machine (1.000015))'Image);
Put_Line (Float'Exponent (Float'Machine (1.000015))'Image);
end Show_Copy_Sign_Leading_Part_Machine;
```

### Model-oriented attributes

In this section, we discuss model-oriented attributes. Depending on the programming languages you're accustomed to, the notion of a "model" of arithmetic might sound unfamiliar. This is how the Ada Reference Manual defines it:

In the Ada Reference Manual

#### Attributes: `'Model_Mantissa`

, `'Model_Emin`

The `'Model_Mantissa`

attribute is similar to the `Machine_Mantissa`

attribute, but it returns the number of bits for the mantissa based on the
underlying numeric model for floating-point operations.

Attention

We can only say that `'Model_Mantissa`

returns the "number of bits" of
the mantissa if `Machine_Radix`

is equal to two. As this is typically
the case for most machines, this simplification is acceptable. However,
if `Machine_Radix`

is *not* equal to two, we're talking about "number
of digits" in the `Machine_Radix`

.

The `'Model_Emin`

attribute is similar to the `Machine_Emin`

attribute, but it returns the minimum machine exponent based on the underlying
numeric model for floating-point operations.

Let's see an example:

#### Attributes: `'Model_Epsilon`

and `Model_Small`

`'Model_Epsilon`

is an attribute that returns the
epsilon of the underlying
numeric model. For example, for the `Float`

type, the `Model_Epsilon`

corresponds to 2^{-23} on a typical desktop PC. (Here, 23 comes from the
mantissa, 24 bits, minus the sign bit.)

`'Model_Small`

is an attribute that returns the smallest value
representable with the underlying numeric model. It corresponds to
`Machine_Radix ** (-Model_Emin - 1)`

. For example, for the `Float`

type, this roughly corresponds to
`Float (Float'Machine_Radix) ** (Float'Model_Emin - 1)`

, or
2^{(-125 - 1)}. Note that the result of this calculation is of
`Float`

type, while the result of `Float'Model_Small`

is a universal
real.

Let's see some examples:

Todo

Add discussion about `'Model`

.

```
with Ada.Text_IO; use Ada.Text_IO;
procedure Show_Model_Epsilon_Small is
begin
Put_Line (Float'(1.000015)'Image);
Put_Line (Float'Model (1.000015)'Image);
end Show_Model_Epsilon_Small;
```

#### Attributes: `'Safe_First`

and `Safe_Last`

The `Safe_First`

and `Safe_Last`

attributes return the safe range of
a type based on the underlying numeric model. As indicated by the Ada Reference
Manual, this is the range "for which the accuracy corresponding to the base
decimal precision is preserved by all predefined operations."

Let's see a code example with these attributes and compare them to the
`'First`

and `'Last`

attributes:

When comparing `Float'First`

to `Float'Safe_First`

, we see that the
values are similar. However, `Float'Safe_First`

has the precision of a
universal real, while `Float'First`

is limited to the precision of the
`Float`

type.

## Fixed-Point Types

In this section, we discuss various attributes and operations related to fixed-point types.

In the Ada Reference Manual

### Attributes of fixed-point types

#### Attribute: `'Machine_Radix`

`'Machine_Radix`

is an attribute that returns the radix of the hardware
representation of a type. For example:

Usually, this value is two, as the radix is based on a binary system.

#### Attribute: `'Machine_Rounds`

and `'Machine_Overflows`

In this section, we discuss attributes that return `Boolean`

values
indicating whether a feature is available or not in the target architecture:

`'Machine_Rounds`

is an attribute that indicates what happens when the result of a fixed-point operation is inexact:`T'Machine_Rounds = True`

: inexact result is rounded;`T'Machine_Rounds = False`

: inexact result is truncated.

`'Machine_Overflows`

is an attribute that indicates whether a`Constraint_Error`

is guaranteed to be raised when a fixed-point operation with that type produces an overflow or divide-by-zero.

#### Attribute: `'Small`

and `'Delta`

The `'Small`

and `'Delta`

attributes return numbers that indicate the
numeric precision of a fixed-point type. In many cases, the `'Small`

of a
type `T`

is equal to the `'Delta`

of that type — i.e.
`T'Small = T'Delta`

. Let's discuss each attribute and how they distinguish
from each other.

The `'Delta`

attribute returns the value of the `delta`

that was
used in the type definition. For example, if we declare
`type T3_D3 is delta 10.0 ** (-3) digits D`

, then the value of
`T3_D3'Delta`

is the `10.0 ** (-3)`

that we used in the type
definition.

The `'Small`

attribute returns the "small" of a type, i.e. the smallest
value used in the machine representation of the type. The *small* must be at
least equal to or smaller than the *delta* — in other words, it must
conform to the `T'Small <= T'Delta`

rule.

Attention

The `Small`

and the `Delta`

need not actually be small numbers.
They can be arbitrarily large. (They could be 1.0, or 1000.0, for example.)

When we declare an ordinary fixed-point data type, we must specify the *delta*.
Specifying the *small*, however, is optional:

If the

*small*isn't specified, it is automatically selected by the compiler. In this case, the actual value of the*small*is an implementation-defined power of two — always following the rule that says:`T'Small <= T'Delta`

.If we want, however, to specify the

*small*, we can do that by using the`'Small`

aspect. In this case, it doesn't need to be a power of two.

For decimal fixed-point types, we cannot specify the *small*. In this case,
it's automatically selected by the compiler, and it's always equal to the
*delta*.

Let's see an example:

As we can see in the output of the code example, the `'Delta`

attribute
returns the value we used for `delta`

in the type definition of the
`T3_D3`

, `TD3`

, `TQ31`

and `TQ15`

types.

The `TD3`

type is an ordinary fixed-point type with the the same delta as
the decimal `T3_D3`

type. In this case, however, `TD3'Small`

is not
the same as the `TD3'Delta`

. On a typical desktop PC, `TD3'Small`

is
2^{-10}, while the delta is 10^{-3}. (Remember that, for ordinary
fixed-point types, if we don't specify the *small*, it's automatically selected
by the compiler as a power of two smaller than or equal to the *delta*.)

In the case of the `TQ15`

type, we're specifying the *small* by using the
`'Small`

aspect. In this case, the underlying size of the `TQ15`

type is 32 bits, while the precision we get when operating with this type is
16 bits. Let's see a specific example for this type:

In the first assignment, we assign `TQ15'Small`

(2^{-31}) to
`V`

. This value is smaller than the type's *delta* (2^{-15}). Even
though `V'Size`

is 32 bits, `V'Delta`

indicates 16-bit precision, and
`TQ15'Small`

requires 32-bit precision to be represented correctly.
As a result, `V`

has a value of zero after this assignment.

In contrast, after the second assignment — where we assign
`TQ15'Delta`

(2^{-15}) to `V`

— we see, as expected, that
`V`

has the same value as the *delta*.

#### Attributes: `'Fore`

and `'Aft`

The `'Fore`

and `'Aft`

attributes indicate the number of characters
or digits needed for displaying a value in decimal representation. To be more
precise:

The

`'Fore`

attribute refers to the digits before the decimal point and it returns the number of digits plus one for the sign indicator (which is either`-`

or space), and it's always at least two.The

`'Aft`

attribute returns the number of decimal digits that is needed to represent the delta after the decimal point.

Let's see an example:

As we can see in the output of the `Dec`

and `Fix`

variables at the
bottom, the value of `'Fore`

is two for both `T3_D3`

and `TQ31`

.
This value corresponds to the length of the string "-0" displayed in the output
for these variables (the first two characters of "-0.123" and "-0.0000000005").

The value of `Dec'Aft`

is three, which matches the number of digits after
the decimal point in "-0.123". Similarly, the value of `Fix'Aft`

is 10,
which matches the number of digits after the decimal point in "-0.0000000005".

### Attributes of decimal fixed-point types

The attributes presented in this subsection are only available for decimal fixed-point types.

#### Attribute: `'Digits`

`'Digits`

is an attribute that returns the number of significant decimal
digits of a decimal fixed-point subtype. This corresponds to the value that we
use for the `digits`

in the definition of a decimal fixed-point type.

Let's see an example:

In this example, `T3_D6'Digits`

is six, which matches the value that we
used for `digits`

in the type definition of `T3_D6`

. The same logic
applies for subtypes, as we can see in the value of `T3_D2'Digits`

. Here,
the value is two, which was used in the declaration of the `T3_D2`

subtype.

#### Attribute: `'Scale`

According to the Ada Reference Manual, the `'Scale`

attribute "indicates
the position of the point relative to the rightmost significant digits of
values" of a decimal type. For example:

If the value of

`'Scale`

is two, then there are two decimal digits after the decimal point.If the value of

`'Scale`

is negative, that implies that the`'Delta`

is a power of 10 greater than 1, and it would be the number of zero digits that every value would end in.

The `'Scale`

corresponds to the N used in the `delta 10.0 ** (-N)`

expression of the type declaration. For example, if we write
`delta 10.0 ** (-3)`

in the declaration of a type `T`

, then the value
of `T'Scale`

is three.

Let's look at this complete example:

In this example, we get the following values for the scales:

`TM3_D6'Scale = -3`

,`T3_D6'Scale = 3`

,`T9_D12 = 9`

.

As you can see, the value of `'Scale`

is directly related to the *delta*
of the corresponding type declaration.

#### Attribute: `'Round`

The `'Round`

attribute rounds a value of any real type to the nearest
value that is a multiple of the *delta* of the decimal fixed-point type,
rounding away from zero if exactly between two such multiples.

For example, if we have a type `T`

with three digits, and we use a value
with 10 digits after the decimal point in a call to `T'Round`

, the
resulting value will have three digits after the decimal point.

Note that the `X`

input of an `S'Round (X)`

call is a universal real
value, while the returned value is of `S'Base`

type.

Let's look at this example:

Here, the `T3_D3`

has a precision of three digits. Therefore, to fit this
precision, 0.2774 is rounded to 0.277, and 0.2777 is rounded to 0.278.

## Big Numbers

Relevant topics

Todo

Complete section!