PROPOSALS FOR 'NUMERIC CLASSES'
AND GENERAL ROUNDING COMMAND

by:   J.H.M. Bonten


First date of publication: 29 january 2007
Date of first modification: 06 january 2010
       (Inheritance between numbers renewed)

Contents:

Back to index of numeric formats


OBJECT-BASED ROUNDING COMMAND

Rounding command leads to thinking in objects

This proposal for the rounding command in Cobol shows that two systems can be designed for defining the domain of influence of this command, the local (= L) and the global (= G) system. In the local system the command influences only those arithmetic operations that are applied in the source text of the program segment wherein that command is written. The segments called by this one are not influenced at all.

In the global system also the called segments can be influenced by the rounding command. This mechanism enables the user (e.g. programmer) to select the rounding mode only once in the main program and then hold it for all called segments. When he selects another rounding mechanism in the main program then all arithmetic operators in all called segments do it that other way also, without any change in the source code and even in the executable code of these segments.

This 'percolating command' can be implemented fairly easily with the help of the Object-Oriented problem-approach. This is described in five steps and with the help of C++.  First the general concept of the classes and objects is changed considerably by an extension. Then the numeric types are seen as such extended classes. Hereafter the command for selecting the rounding mechanism is built in. Alas, by this way this very influential command gets too much freedom. Therefore its application is confined in the fourth step. A fifth step is added to apply this new rounding command in Cobol.

The vision of numbers being objects can ease also the handling of the error messages and that of the different number-lengths. This is described in the next chapter. The last chapter proposes a new kind of mixed-mode arithmetic between all numeric types that already exist in every computer, also with the help of the thinking in objects.

Step 1: Extension of the C++ class

At present a C++ class describes an ordinary C structure that can be accessed only by the set of subroutines that also are mentioned in that description, the so called 'methods'. They protect the structure from other accesses by the outside world. Thus a C++ class is a C-type plus its protecting subroutines.

An object or instance of such a class is the C++ equivalent of a C-type variable. Each object is given an individual copy of the C structure, but it must share the methods with all other instances of its class. The methods exist only once. Thus the passive data can be multiplied as many times as one desires, whilst the active code is available only once.

The proposed extension of the C++ class is the addition of passive data that also exists only once and is shared by all objects. The structure of that data looks like an ordinary C structure. Thus the structure definition contains two parts of data, the 'old' individual part and the 'new' common part. An example of such an extended class might be:

  class Egypt { int Cairo ; float Suez ;
    common: char Gizah ; int Luxor ;
    public:
      BlueNile ( ... argument list ... )
      WhiteNile ( ... argument list ... )
      SuezCanal ( ... argument list ... )
  };

The extension is the COMMON part. The elements of it, Gizah and Luxor, exist only once. They are created at the very moment the class is described. So they already begin to exist even when no object exists. They are exactly equal to the Static variables in Java. They are similar to the global variables in Algol, but now in a protected mode. Also the common part can be seen more or less as the Common block in Fortran.

The methods have free access to these elements, and thus can exchange shared data. Generally direct access from outside the class is prohibited. In the few cases they are made public and thus this access is allowed they have to be named as:
           ClassName.ElementName
not as:
           ObjectName.ElementName

In our example this naming of the common elements becomes:
           Egypt.Gizah      and      Egypt.Luxor
Never it will be
           John.Gizah       and      John.Luxor       or
           Mary.Gizah      and      Mary.Luxor
when John and Mary are instances of the class Egypt.

This is in contrast with the useage of the elements Cairo and Suez, which must be when made public:
           John.Cairo       and      John.Suez       and
           Mary.Cairo      and      Mary.Suez
Erroneous is:
           Egypt.Cairo      and      Egypt.Suez

Step 2: Consider the numbers as objects

In the languages C, C++ and Java the numerical data like the integers and the floats are seen as 'primitive' or 'elementary' data. They are treated differently from the real object data. Nevertheless they too should be seen as objects.

Factly a numeric variable is an instance of the 'class' float, decimal or integer. It is a C-like structure consisting of a series of bits. Generally these bits are not accessed directly by the user. Only the arithmetic operators, like assignment, addition and multiplication, have free access to these bits and read and rewrite them. Only they stir and shake the bits in the variable, and more or less protect them from the outside world. Thus they operate like class-'methods'. They are the methods of the 'classes' float, decimal and integer.

In theory it does not matter whether the arithmetic operators are implemented in the hardware of the computer or in the software. Simply stated, they are subroutines that are called by a program and alter the bits in the numeric objects. Similar to the methods in an 'official' C++ class they exist only once since each of the operators is built only once in the hardware of the CPU (except when the computer has two or more CPU-s). But they can operate on billions of numeric variables which are the individual object instances.

Since the primitive types also can be seen as classes the common part can be added too. Therein a small integral value can be stored that represents the rounding mode. Every arithmetic operator reads this mode before it executes its mathematical operation. One method is added to the class that sets this rounding mode. A second method is added to read this mode. Both do not perform any calculation.

Thus from now on float is a class that somehow looks like:

   class float { bit OutputValue[32] ;
     common:  int Roundmode ;
     public:
       void ROUNDINGSET (int modus) { Roundmode = modus ; }
       int  READROUNDMODE () { return Roundmode ; }
       void ADD    ( float* ; float* )(...}
       void SUBTR  ( float* ; float* ){...}
       void MULT   ( float* ; float* ){...}
       void DIVID  ( float* ; float* ){...}
       void ASSIGN ( float* ){...}
   };

The other numeric classes look similarily. Note that the common part can contain more elements than Roundmode only.

One should imagine that the compiler translates an expression like C = A + B into C.ADD(A,B) before it converts it into the machine code. Of course the actual way applied by the compiler is much simpler, but that should be seen as a kind of shortcut in the implementation, not in the basic thinking about these numeric classes.

An expression chain like C = A*B + D/E needs implicit temporary variables similar to those, TEMP and TEMPS[], that are proposed for explicit use in Cobol. They also meet the description of the numeric class. Again, they are a matter of implementation, not of the basic thinking.

Step 3: Apply the rounding command

The rounding mode applied by all elementary operators is controlled by the input value of Roundingset. The following list is a proposal for this controller:

value of
modus     type of rounding
-----     ----------------

>full rounding = always rounding ...:
  0    down    = towards zero
  1    up      = away from zero
  2    floor   = towards -Infinity
  3    ceiling = towards +Infinity
  4    even    = to last bit (or: digit) even
  5    odd     = to last bit (or: digit) odd

>half rounding = round to nearest, if equidistant round ...:
  8    halfdown    = ...... down
  9    halfup      = ...... up
 10    halffloor   = ...... to floor
 11    halfceiling = ...... to ceiling
 12    halfeven    = ...... to even
 13    halfodd     = ...... to odd

This list is given also in the Proposals for the Cobol numerics. Five of these rounding modes are nearly not used at all. Some others have got special names. These modes are:

value of
modus     type of rounding
-----     ----------------
  0    down     = truncation
  9    halfup   = European rounding
 12    halfeven = banking rounding
  4    even        = [unused]
  5    odd         = [unused]
 10    halffloor   = [unused]
 11    halfceiling = [unused]
 13    halfodd     = [unused]

An example of the statement that sets the type of rounding for all floating-point operations that are executed in the time after its call is:
           float.ROUNDINGSET(5)

Similarily the operators of the class decimal obey their own rounding command. An example of that command is:
           decimal.ROUNDINGSET(4)

Everytime and everywhere in the main program and in every subroutine the programmer can detect which way of rounding is active. For this he can read that mode by using the method Readroundingmode. This method does not change anything in the numeric class. Its result is an integral value:
           int WhichBinRound, WhichDecRound;
           WhichBinRound = float.READROUNDINGMODE ( ) ;
           WhichDecRound = decimal.READROUNDINGMODE ( ) ;

When the mode settings in the two previous small examples hold, then WhichBinRound gets the value 5 and WhichDecRounds gets the value 4.  Note that the rounding mode in the one numeric class works independently from the modes in the other numeric classes.

Actually the arithmetic operators are implanted in the hardware of the computer, otherwise the machine would work much too slowly. For the same reason the integer number Roundmode should be implanted in the hardware. Then the operators can look at it very quickly. For this a separate register should be built in the arithmetic part of the CPU. Thus Roundmode is even faster than the so-called Static or Register variables in C/C++.

In fact several Roundmode registers should be built in the CPU, one for each numeric class. This would result in at least two registers, one for the binary floats and the other for the decimals. The language Cobol will even require more registers; see later. Each of the registers contains at least four bits.

At the end of this document it is shown that the floats of all available lengths (16, 32, 64, 80 and 128 bits) can be seen as belonging to one and the same arithmetic class, the class floats. So the arithmetic operations on all of them are controlled by the same roundmode register. The same holds for the operations on the decimals of all available lengths, and in Cobol for the other numeric data types.

Step 4: Too much freedom, so use a stack

Fool-unproof freedom

Note that this mechanism is not fool proof. Everywhere in the program and in every called subroutine at every calling level the rounding-mode setter can be called. From then on the new rounding mode is applied for every operator in every subroutine and in the main program until the mode setter is called again.

This behaviour equals that of the ordinary global variables in Algol. The value in such a variable keeps its influence throughout the whole program until it is relieved by another value. This relief can be done by every subroutine at every calling level.

What to think about:
           for (int ID=0, 7){ float.ROUNDINGSET (ID); C = C + epsilon; }
wherein epsilon is a value very tiny compared to C. How far does C increase in value? Is it less than 8*epsilon or even more?

Clearly, the rounding mode has a very widespread influence on the program and has very much, too much, freedom for being changed. A foolish designer of a subroutine can mess around in the main program he never has designed or even has seen. This is absolutely fail-unsafe. Therefore the freedom for setting it should be confined. Reading the rounding mode can keep its freedom in being called since it cannot harm anything.

Rounding-commands on stack

For the confinement a stack is introduced. This stack is not called so often that it should be implemented in the hardware. It can stay in the software. For every numeric class a separate stack is used.

The new rounding command system becomes:

The setting of the rounding modes can be done only at the beginning of the main program and the subroutines, prior to the specification of the local variables. It cannot be changed in the midst of the executable statements. The setting commands now look somehow like those in the proposal for Cobol:
           BINARY_ROUNDING_MECHANISM = integer_value ;
           DECIMAL_ROUNDING_MECHANISM = integer_value ;
In stead of the integer value the name of the rounding method can be used, e.g. "halfeven" or "ceiling".

When the flow of the program meets this command statement the old value in the rounding-mode register is pushed on the stack. The register is filled with the new value. This value stays operative until the program flow meets again such a command at the beginning of a newly called subroutine. Then the register is pushed again on the stack prior to receiving the new value.

As long as the program flow does not meet a new command it does not push a value on the stack. The rounding-mode value present in the register stays in use.

When the program flow comes to the end of a subroutine having a rounding command the stack is popped-up and its top value is written in the command register. So the rounding mode that was valid during the run of the subroutine is forgotten and the mode valid immediately prior to the call for that subroutine becomes valid again.

When the program flow comes to the end of a subroutine that not has a rounding command nothing will happen. The stack does not pop-up and the mode-value present in the register stays in use.

In the Proposals for the Cobol numerics this stack mechanism is described as the "Global (in contrast to Local) reach of the rounding command".

Note that the rounding-mode commands and the stacks of the different numeric classes are operated independently. So one can change the mode of the binary class (and thus push its old mode on the stack and later pop it), whilst the mode of the decimal class is kept unchanged.

This mechanism is also applicable in Cobol. Then it works in the exactly equal way. But its application may become very complex for the programmer. So in Cobol a practical simplification should be made.

Step 5: The rounding command in Cobol

It may be expected that in Cobol the data of the different numeric classes are used more intermixedly than in C/C++. Also Cobol has more classes since it applies DISPLAY and NIBBLE. For each of these classes a separate command register and a stack are required.

The hughe bunch of different rounding commands may confuse the user. So the rounding commands of all numeric classes should be combined into one command:
           ROUNDING METHOD IS name.
This must stand at the beginning of the program segment, e.g. in the Special-Names paragraph. See also the Proposals for the Cobol numerics.

At the tail side of an executable Cobol statement the programmer can write:
           ROUNDING = name.
Then for the duration of that statement the new command holds. It also holds for all program segments called by that statement, except when these segments have their own command.

Thus the commands are called simultaneously always. Also the stacks are pushed and popped simultaneously. Nevertheless they should not be reduced to only one register and one stack. That will give problems when Cobol programs and C/C++ programs are linked together.

Since the registers and stacks of the different numeric classes are not joined together and linkage with C/C++ is possible the user must be able to read the command setting for every numeric class separately. The statements might be:
           READROUNDMODE BINARY INTO variable.
           READROUNDMODE DECIMAL INTO variable.
           READROUNDMODE DISPLAY INTO variable.
           READROUNDMODE NIBBLE INTO variable.

When no numeric class is given the register of DISPLAY is read:
           READROUNDMODE INTO variable.

When linkage with other languages is not applied then the four registers and the four stacks always have the same contents. Then the reading statement without explicit class suffices.

Back to contents


MORE ADVANTAGES OF THE NUMERIC CLASSES

Introduction

The concept that the primitive types should be seen as object classes can even be improved. A well-chosen class structure enables the floating-point numbers of all available lengths (16, 32, 64, 80 and 128 bits) to belong to one and the same arithmetic class, the class 'floats'. So the short and long numbers can be used intermixedly. Moreover, the class idea eases the handling of the errors and special values.

The same things hold for the decimals and the integers of all available lengths, and in Cobol for the other numeric data types like Display and Nibble. For example, the proposal for the fixed-point useage of a decimal number shows that in such a number a lot of information can be written that cannot be changed by some arithmetic operators. At first sight it is illogical for an operator to look at the output variable in advance, but in reality it is not. Remarkably, also this fact corresponds with the concept of the object classes.

The object idea and its advantages are explained now.

Giant data field with controllers

To understand the concept let us assume that a numeric value is written always as a sequence of digits and one decimal point. There is no exponent notation. This number is stored in an enormously giant datafield that is infinitely long in theory. In practice it can contain 10,000 or even a million digits. The decimal point is put always exactly in the midst of this field. The unused digits are set always to zero.

Because of the hughe size 'never' an overflow or underflow will occur. This field is embedded in an object structure wherein it is flanged by a few controllers of ordinary size. These controllers settle the contiguous part in it that is allowed to be used, e.g. the part's size or location or the maximum allowed value, and so on.

Thus the controllers restrict the area that can be used in the giant field. The digits outside this allowed area are made zero. For example: "Use only the digits at the right side of the decimal point". This means: only these digits are allowed to be non-zero. Those at the left side must be zero always.

Examples: Setting the area size means the number is floating point. Fixating the location of the area's right digit means the number is fixed point. The language Fortran-90 enables the setting of the maximum allowed value.

Then total data structure to store a numeric value becomes:
           class NumberStore { digit giant_field[1E9] ; int control_1 ; int control_2 ; .... };
In a real application the name NumberStore of this example must be replaced by the name of an actual numeric data class, like Float, Decimal, Nibble or Display.

The arithmetic operators, like addition, subtraction and so on, and also the assignment are the 'methods' that operate on the instances of this class. Theoretically they are allowed to change the contents of every field in the structure, both the giant field and all controller fields. But they all are designed such that they change the data field only. They only read the controller fields. Thus these fields steer the behaviour of these arithmetic operators.

Only one method changes the controller fields: the constructor. For this it gets external values to set the controller fields. Often it simply copies the values to these fields. Also it fills the digits in the giant field, the unused ones with zero. For example, the call to such a constructor might look like:
           NumberStore ( 16, 3, 8964502197365.999, 7.3, ... )
      // set size (16 digits) and location (3 digits at right of point) of the writeable area, set the maximum allowed value, and set the initial value to 7.3.

The rethinking into numeric objects explains why the operators can look inside the output variables before writing a new value. They have to look at the controllers. The rethinking also eases the understanding of the exceptional cases.

Exception handling

If the result of an operator does not fit in the area allowed for receiving that value (i.g. exceeds its maximum or minimum) an escape mechanism must be called. This mechanism tells what to do in such a troubulous situation and how to continue a proper program operation. Therefore the handling of such exceptional situations can be designed using the concept of the object classes too.

The storage of the special values can be implemented easily by the object thinking. For these values an extra field is added to the data structure. This field can be changed by every arithmetic operator. It is the first field to be looked at. The value in this special-values field is interpreted as:

The constructor method is extended with an extra argument, and so a call to it might become for example:
           NumberStore ( 4, 16, 3, 8964502197365.999, 7.3, ... )
      // Size, location and maximum value of the number are set, and the number is equipped with a signaling NaN (= '4').

Note that up to now every instance can get its own control values. Even the instances in one and the same array do not need to have the same control values!

Saving memory

In order to save space in the core memory and on disk the mammoth data structure is compressed such that it fits in an ordinary computer word of e.g. 32 or 64 bits. To enable this space reduction the giant field and the controllers are intertwined and the exponent notation is often applied. Also all numbers in an array must have the same size. Thus an actual array is never a mixture of 16 bits, 32 bits and 64 bits words.

From the above it can be derived that the word length of the numeric variables (i.e. their number of digits or bits) is not decisive for the membership to a class. The variables can be seen as having different controller values only. For example, long_float and short_float belong to the same class float.

Finally one should imagine that the memory saving is accompanied with a renaming of terms. The terms used in the realm of the object oriented programming are replaced by those in the realm of the programming with the primitive types:

The object oriented thinking also enables the implementation of the inheritance between the different numeric classes. This is elucidated in the the next chapter.

Back to contents


INHERITANCE BETWEEN THE NUMBERS

Reject coercions, accept inheritance!

In many languages often mixed-mode arithmetics is applied. This means that fields of different numeric classes can be combined together into one statement.  E.g. an integer value is copied into a float field or vice versa. In such cases the bit pattern has to be changed always. There are rules for this conversion such that the bit pattern in the receiving field represents the value most near to the value represented by the bit pattern in the sending field. The bit patterns themselves can be fully different. This happens in most computers when one field is an integer and the other is a float.

The main two ubiquitous concepts of mixed-type arithmetic are narrowing and widening, i.e copying a float to an integer and vice versa. In Algol-68 these conversion rules are called 'coercions'.

The thinking in numeric classes enables the introduction of a much more concise building of the mixed-mode arithmetics. This text proposes to apply some of the coercion rules whilst not the others. They should be forbidden. For this sake the numerical classes are put in a tree of inheritance. The concept of inheritance enables a much easier definition of the arithmetics between the different numeric types. Herein some 'friend functions' may be needed when different classes are used simultaneously in one arithmetic expression.

Remarkably in this tree the integers inherit the properties of the floats and the floats inherit the properties of the complexes. This direction of inheritance is quite the reverse of what one might expect. As we know in the theory the integers can contain less values than the floats and the floats can contain less values than the complexes, as shown now.

A complex variable actually is a series of two float variables, but theoretically it is independent from that class. A float variable is a complex variable with a zero imaginary part. So it can contain only a subset of all complex values. Therefore the old name of this class was 'real', which is still used by Fortran. (Actually the best name for this class would be: 'exponented'.)

Similarily (in theory) an integer variable allows only a subset of the values a float variable can contain.  A 32-bits integer can be seen as a 40 bits float with the 8-bits exponent fixed at zero (or at coefficient_length-1).

The integers can be the input of several operators that cannot receive the floats and the complexes. Some of these operators are the factorial N!, the integer division DIV and the integer remainder MOD(-ulo).  In most computers the second argument of the exponentiation A**B (= A^B) must be integral. Herein the integer is allowed only because it has not 'difficult' values.

In spite of its smaller set of values the class integer is said to be heir of the class float. Similarily the claas float is said to be heir of the class complex. This may look weird since it seems to be coupled to a kind of narrowing, whilst all present-day programming languages primarily support widening.

Actually the inheritance system is coupled to widening. It enables an integer value to be copied to a float variable implicitly, but not the reverse. Then an explicit conversion routine must be used. Also a float value can be copied implicitly to a complex variable, but not the reverse without explicit conversion routine. Thus the half of the implicit conversions is rejected, viz. those of the narrowing.

Thus the concept of inheritance protects the integers and floats from receiving float and complex values respectively. Often that would stall the program. This makes the mixed-mode arithmetic much more simple to understand and easier to manage.

Also the inheritance tree is more strict than the coercion system. This has many advantages when more numerical classes are included into one programming language. Alltogether there exist many numerical classes more than most languages have, even so many that the application of coercions would give a confusing and ill-digestable mess. This mess becomes even more unmanegeable when the difference between the constants and the variables is taken into account.

Only a well-designed system of inheritance rules can manage properly the hughe bunch of classes thus arising. Both the classes and the rules are discussed elaborately now.

Splitting a type into many classes

In many languages the functions (often called subroutines or subprograms) have input arguments and output arguments. The input arguments accept variables and constant numbers. The output arguments accept variables only. In the proposal the variables and constants are of different numerical classes, although they accept the same set of values and store them in the same bit patterns. Thus each numerical type of the present-day languages is split up in two (or sometimes even more, see later) subtypes that will be handled as individual classes. The syntactical relation between these classes is inheritance. So each 'old' type embraces two (or more) new classes.

For the ease of the programmer the names of these two classes are chosen very conservatively: 'Constant' and 'Variable', not quite innovative names. The inheritance between both classes makes that when the formal argument of a function is of class Constant, then the actual argument can be of the same class Constant or of the heir class Variable. Of course, this was the old input argument. When the formal argument is of class Variable, then the actual argument must be of the same class. Of course, this was the old output argument or in/out argument.

In spite of their names these classes make the old thinking about input and output arguments of a function superfluous. The programmer simply has to know the properties and methods of each class in order to know what he can do with its 'instances' (= the numbers in the class).

The constant data item cannot be changed during its lifetime, so it can be filled only at its creation. The contents in the variable data item can be changed at every moment during the lifetime of that item.

Everywhere in the program where a constant data item is asked, a variable item can be entered. The reverse is not true. So there is a relation of inheritance between these two items:
           variable is heir of constant
In scheme:
           constant << variable

In the representation internal in the computer words there is no difference between the two kinds of data Both they show exactly the same bit pattern for the same numerical value. In fact the computer's hardware does not discern between them. Only the software makes them different.

The constant literal is a special version of the constant class. In some locations in the program text such literal is obligatory, e.g. when giving the boundaries at the creation of a new array. Often in such case a name is prohibited since the compiler wants to see that value immediately. Therefore the literal becomes a separate class that inherits the properties of the named constants. In scheme:
           constant << constant-literal

The omputer's hardware discerns the two basic ways of storing a number, decimally or binarily. The numerical input and output of the program are written always in a decimal way since the human users must enter and read them easily. Similar holds for the constant literals in the program's source text. They are written always in a decimal way. The binary literal does not exist. But, the computer calculates faster when the numeric values are represented binarily.

Especially for the constant literals an inheritance relation must be designed between the decimal and binary representation. Then the programmer can write a decimal number in the source text everywhere a binary number is required for input. This is similar to the practice usual in the present-day languages.

The reversed inheritance must stay impossible since often the computer must know how the programmer wants the number to be stored. For that the programmer must give layout instructions similar to the FORMAT clause in Fortran. So the scheme of this inheritance becomes:
           binary << decimal

Thus a single data type like used in the current languages is split up into five separate classes with inheritance relations in between. In scheme:

       BASIC STRUCTURE OF NUMERICAL INHERITANCE


       BINARY                     DECIMAL
   _______|_______    _______________|_______________
  /               \  /                               \

             |                         |
   bin       |                         |        dec
   typ       |          typ-lit        |        typ
   var       |          9999999        |        var
      \      |                  \      |      /
        \    |                    \    |    /
          \  |                      \  |  /
            bin                       dec
            typ <------------<------- typ
            con                       con
             |                         |
             |                         |

          \_______________________________/
                         |
                     CONSTANTS

===LEGENDA===
bin = binary
dec = decimal
var = variable
con = named constant
lit = constant literal
typ = any current numeric type

In this scheme the series of digits '9' show that the literal never is given a name, but it is notated in the program text by a decimal representation of its value.

The inheritance relations discussed above are shown by four lines. Each of the three slanted lines represents a relation that goes dowmwards in the drawing. The horizontal line shows the relation from the decimal to the binary numbers. The vertical lines through the constants show some inheritance relations that will be discussed later.

Several current types

The numerical data can be distinguished into groups also by a mathematical criterion: their meaning. Thus we get four groups:
           - integer
           - real
           - cartesian complex
           - polar complex

In the theoretical mathematics there exist many more real numbers than integer numbers. So the set of integers is seen as a subset of the reals. In their turn the reals are a subset of the cartesian complex numbers. And in their turn the cartesian complex numbers are a subset of the polar complex numbers. The reason for this is that the cartesian numbers always have their phase angle between 0 and 360 degrees. The polar numbers can have any angle, even of a million degrees or more.

Consequently, for example, an integer value can be entered anywhere a complex value is required for input. But the reverse gives troubles. What to do with the imaginary part when this is not zero?  And what to do with the fraction in a real number when that is not zero?

Therefore an inheritance relation exists between these groups. Everywhere in the program where a complex value is required as input, there a real value or even an integral value can be entered. Where a real value is required as input an integral value can be entered. Also the cartesian complex type and the polar complex show up a relation of inheritance. Thus the total scheme becomes:
           polar-complex << cartesian-complex << real << integer

Remarkably, the numbers of these different groups are stored by different ways in the computer words. This means that each group has its own algebraic interpretation of the bit pattern in a computer word. So for the computer's hardware it makes sense to know which group is intended.

Generally this hardware stores the integers by a simple signed-word notation. For the reals two different ways of storage are in use, the 'fixed-point' and the 'floating-point' notation. In the fixed-point notation the numeric value is stored as one series of bits or digits with one fractional period in between. This is the way of notation used by most people when they write down a number on paper, also by bankers and financials.

The floating-point notation uses two series of bits or digits and places them together and adjecent in one computer word. It is a way of notation typical for scientists and technicians. The advantage of this difficult notation is that it can contain the values from a much wider numeric range than the fixed-point notation can, at least in theory. However, there is a price: the calculations become fairly cumbersome. To speed them up the computer's hardware is equipped with a dedicated auxiliary processor that distracts this workload away from the general-purpose CPU.  It is the so-called floating-point processor.

In the mathematics all complex numbers are notated as a series of two real numbers. So the computer must do the same. The two numbers of a cartesian complex item are called the REAL and the IMAGINARY part. This is already done so in many present-day languages, whilst the polar numbers are applied seldomly. These must be stored as a RADIUS plus a PHASE, which is a totally different interpretation of the row of two reals. Although all complex data are double-value numerics, they often must be seen as elementary data, not as a small row of two numbers.

Complex numbers are used only by scientists and technicians, so their two composing reals should be notated in floating-point. Here the fixed-point notation can be discarded. When the group of the single reals is split up into these two notations, then the total scheme of the inheritances expands to:
           polar-cmplx << cartesian-cmplx << float << fixed << integer

Four of the five groups mentioned in this line correspond more or less with the data types used in the current programming languages. Only the polar-complex group is new. Thus we have got five different data types.

Combining all inheritances

One might expect that for each such type the basic inheritance scheme can be applied. Then one should get a total of 5 * 5 = 25 different classes. But life is not as ordered as one might desire. Some new classes have to be added and some disappear.

First: the added classes

As we have seen already, two classes of variability can be made from every basic numeric type . However, even three such classes can be made from the floating-point type, viz:
           - constant
           - constant-exponent
           - variable

The constant data item cannot be changed during its lifetime, so it can be filled only at its creation. Similar holds for the exponent in the constant-exponent item. Therein only the coefficient can change during its lifetime. Only the variable is fully changeable. In this data item every bit can be changed both in the coefficent and in the exponent at every moment. Thus a tiny list with the properties of each class is:

       CLASS               COEFFICIENT     EXPONENT
      constant                fixed          fixed
      constant-exponent     can change       fixed
      variable              can change     can change

Everywhere in the program where a constant data item is asked, a constant-exponent item or a fully changeable item can be entered. Everywhere where a constant-exponent item is asked, a fully changeable item can be entered. The reverse is not true. So there is an inheritance relation between these three items:
           - variable is heir of constant-exponent
           - constant-exponent is heir of constant
In scheme:
           constant << constant-exponent << variable

In the representation internal in the computer words there is no difference between these three kinds of data with different fixedness. They all show exactly the same bit pattern for the same numerical value. In fact the computer's hardware does not discern between them. Only the software makes them different.

So the scheme of the basic inheritance for the floating-point group has to be entended with two classes, binary constant-exponent and decimal constant-exponent. Since the complex types use the numbers of this group, their scheme must be extended too. Actually this might give a lot of trouble. What happens when the first part has a fixed exponent and the second part has a free one?  The all-embracing total scheme of inheritance might explode to more than fifty different classes amd their multi-connections. This is not yet a nice prospect. Luckily this problem can be solved.

Second: the removed classes

In order to remove the weird and peculiar classes we should look at the habits of the two main groups of human conputer users. The first group are the scientists and technicians. They like a wide range of values with high accuracy. Thus they will use the floating-point reals with free exponent. They will not use the fixed-point numbers and the complexes with a fixed exponent. Their computer must perform very many calculations too. In order to gain speed it must work binarily.

The second and much larger group are the bankers and financials and the ordinary people. They do not handle the floating-point notation and the complex values. They like the fixed-point notation. Also they do not like 'unexplainable' rounding errors. For these people the computer must calculate decimally.

Thus the fixed-point notation is in use very ubiquitously. Its most widespread version is the decimal Cobol-'type' DISPLAY or NIBBLE (see elsewhere in this internet site). The analogous binary type is not in use by the scientists and technicians. Therefore it is abandoned.

For the machine itself it makes sense when the lengthy fixed-point notation can be compressed into the more concise float notation without the loss of information about the position of the fractional point. Therefore the constant-exponent class makes sense for the decimals. The binary version of it is useless and so omitted.

The decimal-float notation consists of a decimal coefficient and a decimal or even binary exponent, e.g. the Packed Decimal Encoding IEEE-754-2008.  This notation is apt for use with a constant exponent, like stated elsewhere in this internet site.

Another fact is that a complex constant-literal does not exist. It is a composition of two real literals. Therefore the literal class of each complex type can be removed from the scheme.

Third: inheritance via constants only

So, the basic inheritance scheme of nearly every numeric group has to be adapted. After these adaptations the schemes of all five groups are piled together according to the line of the group inheritance. In order to avoid conflicts the connections between these schemes must be made only by connecting the constants via inheritance relations. These are already shown by the two vertical lines in the basic scheme. In these lines the inheritance goes downwards.

In summary the three basic inheritance relations are:
           polar-cmplx << cartesian-cmplx << float << fixed << integer
           constant-literal >> constant << constant-exponent << variable
           binary << decimal

Combining each option in each line with each option in each other line results in a set with a total 40 different classes with very many inheritance relations between them. Luckily some of these combinations are theoretically impossible and many of them are not useful, as has been stated already.

So over the half of the classes can be removed. The final result contains 18 classes and 19 relations. These are shown in the drawing below. Each slanted or vertical line stands for an inheritance relation that goes downwards. The literals are elucidated with a kind of 'picture' in Cobol-style.

Scheme of numerical inheritance

        SCHEME OF NUMERICAL INHERITANCE


     BINARY                         DECIMAL
 _______|_______    ___________________|___________________
/               \  /                                       \

 bin                                          dec
 int                  int-lit                 int
 var                   -99999                 var
    \                         \             /
      \                         \         /
        \                         \     /
          bin                       dec
          int <------------<------- int
          con                       con
           |                         |
           |                         |
           |                         |
           |                         |
           |                         |        dec
           |          fix-lit        |        fix
           |          -999.99        |        var
           |                  \      |      /
           |                    \    |    /
           |                      \  |  /
           |                        dec
           |    ,--<-------<------- fix                 dec
           |   /                    con                 flot
           |  |                      |                  var
           |  |                      |                /
           |  |                      |              /
           |  |                      |            /
 bin       |  |                      |        dec
 flot      |  |      flot-lit        |        flot
 var       |  V      -9.99E+9        |        conexp
    \      |  |               \      |      /
      \    |  |                 \    |    /
        \  | /                    \  |  /
          bin                       dec
          flot <-----------<------- flot
          con                       con
           |
           |
           |
           |
 bin       |
 cart      |
 var       |                         ===LEGENDA===
    \      |
      \    |                         bin = binary
        \  |                         dec = decimal
          bin                      _
          cart                    |  int = integer
          con                     |  fix = fixed point
           |                  typ-<  flot = floating point
           |                      |  cart = cartesian complex
           |                      |_ pol = polar complex
           |
 bin       |                         var = fully variable
 pol       |                         conexp = constant exponent
 var       |                         con = named constant
    \      |                         lit = constant literal
      \    |
        \  |                        EXAMPLE: dec-flot-con means
          bin                        deciaml constant number
          pol                        stored in the same way as
          con                        a floating-point number

        \______________________________/
                       |
                   CONSTANTS

Assignment operations and data lengths

Many languages have the assignment operator. This operator copies the contents of an elementary data item to the memory location of another data item of the same elementary type. In the infix notation this operator is often written as '=' (e.g. Fortran, C/C++) or as ':=' (e.g. Algol60/68, Pascal). Examples:
           FloatB = FloatA
           FloatB = 3.0 * (FloatA - pi^2/log(gamma))

The assignment operator requires that the left operand is a variable (perhaps with constant-exponent), whilst the right operand can be a constant.

This operator holds for numerical data of the same basic structure. It also operates on data of different structures. Then the data coming from the sender must be moulded and transformed severely before they are stored in the receiver. For this in many languages the coercion rules are applied. This proposal advocates the inheritance rules.

Example:
Given the data items:
           BinIntVar IA
           BinFlotVar FB
In the statement
           FB = IA
the datum coming from IA is moulded such that it fits in the variable FB.  The resulting bit pattern differs totally from the incoming bit pattern, but the value it stands for is (approximately) the same. In the statement
           IA = FB
a similar moulding action is performed by the most of the languages. But in this proposal it is forbidden since it does not obey the rules of inheritance.

Therefore it is forbidden to write:
           IntVar = FloatVar
           IntVar = CartesVar
           FloatVar = CartesVar
whilst the opposite is perfectly nice:
           CartesVar = FloatVar
           CartesVar = IntVar
           FloatVar = IntVar
Herein XyzVar means: any variable of class Xyz.

In this proposal in general the assignment operator does not mould the data at all. It simply copies the bit pattern upwards along eight of the the eleven slanted lines. The data are moulded implicitly only when they 'fall downwards' along the inheritance tree.

Example:
           BinCartVar BCV
           BCV = 97
Herein 97 is a decimal integer constant literal. First (in theory) it is transformed into a decimal integer constant. For example, in the case that such a constant has six digits and a sign, then the literal is padded at its left side with four zeroes and a plus sign. Secondly the decimal number thus gained is converted into a binary integer number. At third this binary integer is translated into a number in floating-point notation. At last the namber is made cartesian-complex by adding a zero as the imaginary part. After these four moulds the number now enters the argument list of the assignment operator. Now its bit pattern is copied to the variable BCV without any change.

Thus a number can be moulded many times during its fall down the tree. But the actual assignment operator keeps it unscaved. It performs a small moulding only when it copies a DecFlotCon value to a DecFlotConexp variable in order to keep unscaved the exponent.

Passing to the right via the three horizontal lines is fairly cumbersome. Since the decimal numbers often can be unnormalized the programmer has to enter the value of the exponent and the way of rounding the result. These data are somewhat analog to the formatting data in the Write statement of many languages.

Often a subprogram emits one calculated value via its name. Then it is called a function. This proposal requires the type of this name to be a constant type for the calling program, otherwise the name might serve as an input argument too. In the function's program body this constant value should be assigned by a RETURN statement as the last statement immediately before the END statment, like applied in C/C++.  An example of a function header as perceived by the calling program is:
           BinFlotCon 10_LOG ( BinFlotCon x )

In many programs numerical data of different lengths are used, e.g. short-sized, medium-sized and long-sized floats. A previous part of this document shows that numbers of different lengths can belong to the same class. Each class has built-in routines for expansion, compression and error management. Below it is discussed very shorthandedly.

When the receiving field is as long as the sending field then the bit pattern is copied exactly bit by bit without looking at the meaning of its contents. Many computers copy a bit pattern meaning an error (e.g. NaN) without any notice.

When the receiving field is longer than the sending field, then the bit pattern is expanded such that the same numerical value is stored in the longer data item. Generally this happens by padding the shorter field with zero-valued bits (or: digits).

When the receiving field is shorter, then the bit pattern is shortened to fit. For this sake a special numerical operation is applied, the numerical rounding. A new bit pattern is chosen such that it represents a value the most near as possible (and sometimes equal) to the value of the sending number. That new value is stored. The SET-ROUNDING command determines which kind of rounding is chosen.

In some cases the new value cannot be chosen at all since the original value is too great. This is called an overflow. Then the Infinity pattern is chosen or the message of overflow error is given.

Epilogue

In summary, this propoal makes objects out of the numeric types that are present in every computer and defines inheritances between them. Thus it proposes a very big simplification in the the mixed-mode (or better: mixed-type) arithmetics. This even holds when the numbers of the same type have different lengths.

In stead of 'coercion rules' inheritance can also be applied on the other kinds of elementary hardware-bound data, like the logicals and characters. For example, there are two separate logical classes, the boolean-constants and the boolean-variables.

Back to contents

Back to index of numeric formats