Skip to Content

Maximum positive value that can be stored by integer type variable in ABAP is 2147483647.

For values beyond maximum limit of integer variable, workarounds can be used.

One such example is discussion MOD operation fail for large numbers started by Starlet Abraham

I was able to calculate correct result of 512^17 MOD 2773 using the methods detailed below.

I have written this program to do following operations on large integers.

  • Addition
  • Subtraction
  • Multiplication
  • Division
  • Mod
  • Power

Full code snippet is posted at the end of this document.

Program has its flaws, but it works.

I would briefly go through how the logic works.

How are big numbers stored?

Local class lcl_bignum has instance attributes that store big numbers.

LV_STRING – Number stored as string type.

LT_INT – an internal table of integers. The string is broken into substrings of size 4 (base 10000), and those substrings ( as int4 ) are stored in internal table.

LV_SIGN – integer type to store the sign of number. +1 for positive, -1 for negative.

For example, number 1234567890 will be stored in internal table as

LT_INT
7890
3456
12

Why is number broken into base 10000?

Base 10000 is safe considering the capacity of integer variable type in ABAP.

Computing 9999 * 9999 results in 99980001, which is within the limits of integer type.

On the other hand, 99999 * 99999 results in 999980001, which would lead to overflow exception.

How to create instance and store big number?

Below code snippets can be used to create instance of big number.

The constructor parameters are options.

CREATE OBJECT lr3.

Above code will create instance with zero value in it.

CREATE OBJECT lr2
  EXPORTING
    i_value = ‘533335555511111’.

Above code will create instance and populate lv_string and lt_int by breaking down input string.

Sign of number is positive by default.

Input string should have only numbers in it.

CREATE OBJECT lr1
  EXPORTING
    i_value = ‘6584389034291301828999’
    i_negative = ‘X’.

Using above code, ‘X’ can be passed i_negative flag if we have a negative number.

How to add numbers?

Instance method Add( ) can be used for adding numbers.

It accepts 2 object reference that need to be added, and result is stored in object whose method gets called.

We are adding the numbers starting lowest weightage (LT_INT[1]), and carrying forward extra in case of overflow.

For example, we add 12345678 and 22223333. Program would add (5678,3333) first, and then (1234,2222).

For A = B + C, below code can be used.

lr3->add( i_obj1 = lr1
          i_obj2 = lr2 ).

For A = A + B, below code can be used.

lr3->add( i_obj1 = lr3
          i_obj2 = lr2 ).

How to subtract numbers?

Instance method Subtract( ) can be used for subtracting numbers.

The usage is same as Add( ) method.

Looking at code, you would see that Add( ) and Subtract( ) are calling each other.

It is done the tackle how absolute values are treated depending on their sign.

For example, A = B + C, the values of B and C will be added, and common sign will be retained if B and C have same sign. Both positive, or both negative.

If the signs of B and C are different, we essentially have to keep sign of B, and calculate |B| – |C| (absolute values).

How to multiply numbers?

Instance method Multiply( ) can be used.

It accepts 2 object references, and stores result in calling object.

I would explain the logic using example. It is similar to the method used in elementary school.

For elementary example of multiplying 123 and 456, below logic is used.

123

456

===

3*6 + (3*5+2*6) + (3*4+2*5+1*6) + (2*4+1*5) + 1*4

Same thing is done in program at base 10000 level.

At every stage we are carrying forward the overflow value.

How to divide numbers?

For division, I am guessing the answer by multiplying.

Something like binary search, I start with 1 as answer, keep on multiplying it by 10000 still answer crosses dividend.

Once the guess surpasses dividend, I take a step back, and start multiplying by square root of 10000.

A point comes when multiplier is reduced to 1, and multiplication can no longer be used to get closer to answer.

I then use addition to get closer to answer. Instead of square root, divide by 2 to get next “jump value”.

A dry run using elementary example would be something like this.

451

4

How to do calculate remainder (MOD operation)

Instance method Mod( ) can be used, which internally uses Divide( ), Multiply( ) and Subtract( ).

To calculate A = B MOD C, program does something like:

A = B – C * ( B DIV C )

Here we are reusing the code written in previous methods.

How to calculate Power?

Instance method Power( ) can be used, which internally uses Multiply( ) and Subtract( ).

For calculating A = B POWER C, program starts with 1 as answer, uses loop to multiply B to answer, C number of times.

Full code snippet

  1. *———————————————————————-*
  2. *       CLASS lcl_bignum DEFINITION
  3. *———————————————————————-*
  4. *
  5. *———————————————————————-*
  6. CLASS lcl_bignum DEFINITION.
  7.   PUBLIC SECTION.
  8.     METHODS constructor IMPORTING i_value TYPE string OPTIONAL
  9.                     i_negative  TYPE c OPTIONAL.
  10.     METHODS get_sign RETURNING value(r_sign) TYPE i.
  11.     METHODS get_string RETURNING value(r_string) TYPE string.
  12.     METHODS get_int RETURNING value(r_int) TYPE int4_table.
  13.     METHODS set_data IMPORTING i_value TYPE string
  14.                    i_negative TYPE c OPTIONAL.
  15.     METHODS add IMPORTING i_obj1 TYPE REF TO lcl_bignum
  16.                 i_obj2 TYPE REF TO lcl_bignum.
  17.     METHODS subtract IMPORTING  i_obj1 TYPE REF TO lcl_bignum
  18.                   i_obj2 TYPE REF TO lcl_bignum.
  19.     METHODS multiply IMPORTING  i_obj1 TYPE REF TO lcl_bignum
  20.                   i_obj2 TYPE REF TO lcl_bignum.
  21.     METHODS divide IMPORTING i_obj1 TYPE REF TO lcl_bignum
  22.                  i_obj2 TYPE REF TO lcl_bignum.
  23.     METHODS mod IMPORTING i_obj1 TYPE REF TO lcl_bignum
  24.                 i_obj2 TYPE REF TO lcl_bignum.
  25.     METHODS power IMPORTING i_obj1 TYPE REF TO lcl_bignum
  26.                 i_obj2 TYPE REF TO lcl_bignum.
  27.     METHODS invert_sign.
  28.     METHODS copy_values IMPORTING i_obj TYPE REF TO lcl_bignum.
  29.     METHODS abs_compare IMPORTING i_obj TYPE REF TO lcl_bignum
  30.               RETURNING value(r_result) TYPE i.
  31.   PROTECTED SECTION.
  32.     METHODS int_to_string.
  33.     METHODS split_into_int.
  34.     METHODS delete_leading_zeros.
  35.     METHODS divide_by_2.
  36.   PRIVATE SECTION.
  37.     DATA lv_string TYPE string.
  38.     DATA lv_sign TYPE i VALUE 1.
  39.     DATA lt_int TYPE int4_table.
  40.     “sqrt of max int4 is 46340
  41.     CONSTANTS: gc_unit_size TYPE i VALUE 10000,
  42.            gc_unit_digits TYPE i VALUE 4,
  43.            gc_gt TYPE i VALUE 1“greater than
  44.            gc_lt TYPE i VALUE 2“less than
  45.            gc_eq TYPE i VALUE 0“equals
  46. ENDCLASS.                    “lcl_bignum DEFINITION
  47. *———————————————————————-*
  48. *       CLASS lcl_bignum IMPLEMENTATION
  49. *———————————————————————-*
  50. *
  51. *———————————————————————-*
  52. CLASS lcl_bignum IMPLEMENTATION.
  53.   METHOD constructor.
  54.     IF i_value IS SUPPLIED.
  55.       IF i_value CO ‘ 1234567890’.
  56.         lv_string = i_value.
  57.         CONDENSE lv_string.
  58.       ENDIF.
  59.       split_into_int( ).
  60.     ENDIF.
  61.     IF i_negative IS SUPPLIED.
  62.       IF i_negative EQ ‘X’.
  63.         lv_sign = –1.
  64.       ENDIF.
  65.     ENDIF.
  66.   ENDMETHOD.                    “constructor
  67.   METHOD set_data.
  68.     CLEAR: lv_string, lt_int.
  69.     lv_sign = 1.
  70.     IF i_value CO ‘ 1234567890’.
  71.       lv_string = i_value.
  72.       CONDENSE lv_string.
  73.     ENDIF.
  74.     split_into_int( ).
  75.     IF i_negative IS SUPPLIED.
  76.       IF i_negative EQ ‘X’.
  77.         lv_sign = –1.
  78.       ENDIF.
  79.     ENDIF.
  80.   ENDMETHOD.                    “set_data
  81.   METHOD split_into_int.
  82.     DATA lv_string TYPE string.
  83.     DATA lv_int TYPE i.
  84.     lv_string = me->lv_string.
  85.     WHILE lv_string IS NOT INITIAL.
  86.       IF STRLEN( lv_string ) GT gc_unit_digits.
  87.         SHIFT lv_string RIGHT BY gc_unit_digits PLACES CIRCULAR.
  88.         lv_int = lv_string+0(gc_unit_digits).
  89.         SHIFT lv_string LEFT BY gc_unit_digits PLACES.
  90.       ELSE.
  91.         lv_int = lv_string.
  92.         CLEAR lv_string.
  93.       ENDIF.
  94. *      INSERT lv_int INTO lt_int INDEX 1.
  95.       APPEND lv_int TO lt_int.
  96.     ENDWHILE.
  97.   ENDMETHOD.                    “split_into_int
  98.   METHOD delete_leading_zeros.
  99.     DATA: lt_temp TYPE int4_table,
  100.         lv_temp TYPE i,
  101.         lv_count TYPE i.
  102.     CHECK lt_int IS NOT INITIAL.
  103.     lt_temp = lt_int.
  104.     CLEAR lt_int.
  105.     lv_count = LINES( lt_temp ).
  106.     DO lv_count TIMES.
  107.       READ TABLE lt_temp INTO lv_temp INDEX lv_count.
  108.       IF lv_temp IS NOT INITIAL.
  109.         EXIT.
  110.       ENDIF.
  111.       lv_count = lv_count – 1.
  112.     ENDDO.
  113.     IF lv_count IS NOT INITIAL AND
  114.        lv_count LE LINES( lt_temp ).
  115.       APPEND LINES OF lt_temp FROM 1 TO lv_count TO lt_int.
  116.     ENDIF.
  117.   ENDMETHOD.                    “delete_leading_zeros
  118.   METHOD int_to_string.
  119.     DATA lv_int TYPE i.
  120.     DATA lv_char TYPE n LENGTH gc_unit_digits.
  121.     LOOP AT lt_int INTO lv_int.
  122.       lv_char = lv_int.
  123.       CONCATENATE lv_char lv_string INTO lv_string.
  124.     ENDLOOP.
  125.     SHIFT lv_string LEFT DELETING LEADING ‘0’.
  126.   ENDMETHOD.                    “int_to_string
  127.   METHOD get_int.
  128.     r_int = lt_int.
  129.   ENDMETHOD.                    “get_int
  130.   METHOD get_sign.
  131.     r_sign = lv_sign.
  132.   ENDMETHOD.                    “get_sign
  133.   METHOD get_string.
  134.     r_string = lv_string.
  135.   ENDMETHOD.                    “get_string
  136.   METHOD copy_values.
  137.     lv_string = i_obj->get_string( ).
  138.     lv_sign = i_obj->get_sign( ).
  139.     lt_int = i_obj->get_int( ).
  140.   ENDMETHOD.                    “copy_values
  141.   METHOD abs_compare.
  142.     DATA: lt_int2 TYPE int4_table,
  143.         lv_int TYPE i,
  144.         lv_int2 TYPE i,
  145.         lv_max TYPE i.
  146.     lt_int2 = i_obj->get_int( ).
  147.     lv_max = LINES( lt_int ).
  148.     IF LINES( lt_int2 ) GT lv_max.
  149.       lv_max = LINES( lt_int2 ).
  150.     ENDIF.
  151.     WHILE lv_max GT 0.
  152.       CLEAR: lv_int,
  153.          lv_int2.
  154.       READ TABLE lt_int INTO lv_int INDEX lv_max.
  155.       READ TABLE lt_int2 INTO lv_int2 INDEX lv_max.
  156.       IF lv_int EQ lv_int2.
  157.         lv_max = lv_max – 1.
  158.       ELSE.
  159.         IF lv_int GT lv_int2.
  160.           r_result = gc_gt.
  161.         ELSEIF lv_int LT lv_int2.
  162.           r_result = gc_lt.
  163.         ENDIF.
  164.         EXIT.
  165.       ENDIF.
  166.     ENDWHILE.
  167.   ENDMETHOD.                    “abs_compare
  168.   METHOD divide_by_2.
  169.     DATA: lv_int TYPE i,
  170.         lt_temp TYPE int4_table,
  171.         lv_mod TYPE i,
  172.         lv_count TYPE i.
  173.     lv_count = LINES( lt_int ).
  174.     DO lv_count TIMES.
  175.       READ TABLE lt_int INTO lv_int INDEX lv_count.
  176.       IF sy-subrc EQ 0.
  177.         lv_int = lv_int + lv_mod * gc_unit_size.
  178.         lv_mod = lv_int MOD 2.
  179.         lv_int = lv_int DIV 2.
  180.         INSERT lv_int INTO lt_temp INDEX 1.
  181.       ENDIF.
  182.       lv_count = lv_count – 1.
  183.     ENDDO.
  184.     lt_int = lt_temp.
  185.     delete_leading_zeros( ).
  186.     int_to_string( ).
  187.   ENDMETHOD.                    “divide_by_2
  188.   METHOD invert_sign.
  189.     lv_sign = lv_sign * –1.
  190.   ENDMETHOD.                    “invert_sign
  191.   METHOD add.
  192.     DATA: lt_int1 TYPE int4_table,
  193.         lt_int2 TYPE int4_table,
  194.         lv_int TYPE i,
  195.         lv_int1 TYPE i,
  196.         lv_int2 TYPE i,
  197.         lv_index TYPE i,
  198.         lv_count TYPE i,
  199.         lv_extra TYPE i.
  200.     lt_int1 = i_obj1->get_int( ).
  201.     lt_int2 = i_obj2->get_int( ).
  202.     IF i_obj1->get_sign( ) NE i_obj2->get_sign( ).
  203.       subtract( i_obj1 = i_obj1
  204.           i_obj2 = i_obj2 ).
  205.       EXIT.
  206.     ENDIF.
  207.     lv_count = LINES( lt_int1 ).
  208.     IF lv_count LT LINES( lt_int2 ).
  209.       lv_count = LINES( lt_int2 ).
  210.     ENDIF.
  211.     CLEAR: lt_int, lv_string.
  212.     lv_sign = i_obj1->get_sign( ).
  213.     DO lv_count TIMES.
  214.       CLEAR: lv_int1,
  215.          lv_int2.
  216.       lv_index = sy-index.
  217.       READ TABLE lt_int1 INTO lv_int1 INDEX lv_index.
  218.       READ TABLE lt_int2 INTO lv_int2 INDEX lv_index.
  219.       lv_int = lv_extra + lv_int1 + lv_int2.
  220.       lv_extra = lv_int DIV gc_unit_size.
  221.       lv_int = lv_int MOD gc_unit_size.
  222.       APPEND lv_int TO lt_int.
  223.     ENDDO.
  224.     int_to_string( ).
  225.   ENDMETHOD.                    “add
  226.   METHOD subtract.
  227.     DATA: lt_int1 TYPE int4_table,
  228.         lt_int2 TYPE int4_table,
  229.         lv_int TYPE i,
  230.         lv_int1 TYPE i,
  231.         lv_int2 TYPE i,
  232.         lv_index TYPE i,
  233.         lv_count TYPE i,
  234.         lv_extra TYPE i.
  235.     FIELD-SYMBOLS: <fs_int> TYPE i.
  236.     lt_int1 = i_obj1->get_int( ).
  237.     lt_int2 = i_obj2->get_int( ).
  238.     IF i_obj1->get_sign( ) NE i_obj2->get_sign( ).
  239.       i_obj2->invert_sign( ).
  240.       add( i_obj1 = i_obj1
  241.          i_obj2 = i_obj2 ).
  242.       EXIT.
  243.     ENDIF.
  244.     lv_count = LINES( lt_int1 ).
  245.     IF lv_count LT LINES( lt_int2 ).
  246.       lv_count = LINES( lt_int2 ).
  247.     ENDIF.
  248.     CLEAR: lt_int, lv_string.
  249.     lv_sign = i_obj1->get_sign( ).
  250.     DO lv_count TIMES.
  251.       CLEAR: lv_int1,
  252.          lv_int2.
  253.       lv_index = sy-index.
  254.       READ TABLE lt_int1 INTO lv_int1 INDEX lv_index.
  255.       READ TABLE lt_int2 INTO lv_int2 INDEX lv_index.
  256.       lv_int = lv_extra + lv_int1 – lv_int2.
  257.       CLEAR lv_extra.
  258.       IF lv_int LT 0.
  259.         lv_int = lv_int + gc_unit_size.
  260.         lv_extra = –1.
  261.       ENDIF.
  262.       lv_int = lv_int MOD gc_unit_size.
  263.       APPEND lv_int TO lt_int.
  264.     ENDDO.
  265.     IF lv_extra IS NOT INITIAL.
  266.       lv_sign = lv_sign * lv_extra.
  267.       LOOP AT lt_int ASSIGNING <fs_int>.
  268.         IF sy-tabix EQ 1.
  269.           <fs_int> = gc_unit_size – <fs_int>.
  270.         ELSE.
  271.           <fs_int> = gc_unit_size – <fs_int> + 1.
  272.         ENDIF.
  273.       ENDLOOP.
  274.     ENDIF.
  275.     delete_leading_zeros( ).
  276.     int_to_string( ).
  277.   ENDMETHOD.                    “subtract
  278.   METHOD multiply.
  279.     DATA: lt_int1 TYPE int4_table,
  280.         lt_int2 TYPE int4_table,
  281.         lv_int TYPE i,
  282.         lv_int1 TYPE i,
  283.         lv_int2 TYPE i,
  284.         lv_int_tmp TYPE i,
  285.         lv_index TYPE i,
  286.         lv_index_start TYPE i,
  287.         lv_index_end   TYPE i,
  288.         lv_index_start_tmp TYPE i,
  289.         lv_index_end_tmp TYPE i,
  290.         lv_count TYPE i,
  291.         lv_carry TYPE i,
  292.         lv_extra TYPE i.
  293.     lt_int1 = i_obj1->get_int( ).
  294.     lt_int2 = i_obj2->get_int( ).
  295.     lv_count = LINES( lt_int1 ).
  296.     IF lv_count LT LINES( lt_int2 ).
  297.       lv_count = LINES( lt_int2 ).
  298.     ENDIF.
  299.     CLEAR: lt_int, lv_string.
  300.     lv_sign = i_obj1->get_sign( ) * i_obj2->get_sign( ).
  301.     CHECK lv_count IS NOT INITIAL.
  302.     lv_index_start = 1.
  303.     lv_index_end   = 1.
  304.     DO.
  305.       CLEAR lv_int.
  306.       lv_index_start_tmp = lv_index_start.
  307.       lv_index_end_tmp   = lv_index_end.
  308.       lv_int = lv_extra.
  309.       CLEAR lv_extra.
  310.       WHILE lv_index_start_tmp LE lv_index_end.
  311.         CLEAR: lv_int1,
  312.              lv_int2.
  313.         READ TABLE lt_int1 INTO lv_int1 INDEX lv_index_start_tmp.
  314.         READ TABLE lt_int2 INTO lv_int2 INDEX lv_index_end_tmp.
  315.         lv_int_tmp = ( lv_int1 * lv_int2 ) MOD gc_unit_size.
  316.         lv_extra = lv_extra + ( lv_int1 * lv_int2 ) DIV gc_unit_size.
  317.         lv_int = lv_int + lv_int_tmp.
  318.         lv_extra = lv_extra + lv_int DIV gc_unit_size.
  319.         lv_int = lv_int MOD gc_unit_size.
  320.         lv_index_start_tmp = lv_index_start_tmp + 1.
  321.         lv_index_end_tmp = lv_index_end_tmp – 1.
  322.       ENDWHILE.
  323.       APPEND lv_int TO lt_int.
  324.       IF lv_index_end LT lv_count.
  325.         lv_index_end = lv_index_end + 1.
  326.       ELSEIF lv_index_start LT lv_count.
  327.         lv_index_start = lv_index_start + 1.
  328.       ELSEIF lv_index_start EQ lv_count AND
  329.        lv_index_end   EQ lv_count.
  330.         EXIT.
  331.       ENDIF.
  332.     ENDDO.
  333.     IF lv_extra IS NOT INITIAL.
  334.       APPEND lv_extra TO lt_int.
  335.     ENDIF.
  336.     delete_leading_zeros( ).
  337.     int_to_string( ).
  338.   ENDMETHOD.                    “multiply
  339.   METHOD divide.
  340.     DATA: lt_int1 TYPE int4_table,
  341.         lt_int2 TYPE int4_table,
  342.         lv_int TYPE i,
  343.         lv_int1 TYPE i,
  344.         lv_int2 TYPE i,
  345.         lv_string1 TYPE string,
  346.         lv_string2 TYPE string,
  347.         lv_index TYPE i,
  348.         lv_count TYPE i,
  349.         lv_extra TYPE i,
  350.         lv_sign1 TYPE i,
  351.         lr1 TYPE REF TO lcl_bignum,
  352.         lr2 TYPE REF TO lcl_bignum,
  353.         lr_guess TYPE REF TO lcl_bignum,
  354.         lr_step TYPE REF TO lcl_bignum,
  355.         lv_step_str TYPE string,
  356.         lv_step TYPE i,
  357.         lr_temp  TYPE REF TO lcl_bignum.
  358.     CREATE OBJECT lr1.
  359.     CREATE OBJECT lr2.
  360.     lr1->copy_values( i_obj1 ).
  361.     lr2->copy_values( i_obj2 ).
  362. *   quotient is zero when divisor is bigger than dividend
  363.     CHECK lr1->abs_compare( lr2 ) EQ gc_gt.
  364.     IF lr1->get_sign( ) LT 0.
  365.       lr1->invert_sign( ).
  366.     ENDIF.
  367.     IF lr2->get_sign( ) LT 0.
  368.       lr2->invert_sign( ).
  369.     ENDIF.
  370. * start with 1, keep multiplying by gc_unit_size
  371.     CREATE OBJECT lr_guess
  372.       EXPORTING
  373.         i_value = ‘1’.
  374.     CREATE OBJECT lr_temp.
  375.     CREATE OBJECT lr_step.
  376.     lv_step = gc_unit_size.
  377.     DO.
  378.       DO.
  379.         lr_temp->multiply( i_obj1 = lr_guess
  380.                    i_obj2 = lr2 ).
  381.         CASE lr_temp->abs_compare( lr1 ).
  382.           WHEN gc_eq.
  383.             copy_values( lr_guess ).
  384.             RETURN.
  385.           WHEN gc_gt.
  386.             EXIT.
  387.           WHEN gc_lt.
  388. * increment
  389. *            lv_step = gc_unit_size.
  390.             copy_values( lr_guess ).
  391.             lv_step_str = lv_step.
  392.             lr_step->set_data( lv_step_str ).
  393.             lr_guess->multiply( i_obj1 = lr_guess
  394.                       i_obj2 = lr_step ).
  395. *        when others.
  396.        ENDCASE.
  397.       ENDDO.
  398.       lr_guess->copy_values( me ).
  399.       lv_step = SQRT( lv_step ).
  400.       IF lv_step EQ 1.
  401.         EXIT.
  402.       ENDIF.
  403.     ENDDO.
  404. * answer is between guess and guess * 2
  405. * now add steps to move closer to answer
  406. *    lv_step = gc_unit_size.
  407.     lr_step->copy_values( lr_guess ).
  408.     DO.
  409.       DO.
  410.         lr_temp->multiply( i_obj1 = lr_guess
  411.                    i_obj2 = lr2 ).
  412.         CASE lr_temp->abs_compare( lr1 ).
  413.           WHEN gc_eq.
  414.             copy_values( lr_guess ).
  415.             RETURN.
  416.           WHEN gc_gt.
  417.             EXIT.
  418.           WHEN gc_lt.
  419. * increment
  420.             copy_values( lr_guess ).
  421.             lr_guess->add( i_obj1 = lr_guess
  422.                      i_obj2 = lr_step ).
  423. *        when others.
  424.         ENDCASE.
  425.       ENDDO.
  426.       lr_guess->copy_values( me ).
  427. * quick way to divide by 2
  428.       lr_step->divide_by_2( ).
  429.       IF lr_step->get_int( ) IS INITIAL.
  430.         EXIT.
  431.       ENDIF.
  432.     ENDDO.
  433. *    int_to_string( ).
  434.   ENDMETHOD.                    “divide
  435.   METHOD mod.
  436.     DATA lr_div TYPE REF TO lcl_bignum.
  437.     CREATE OBJECT lr_div.
  438.     lr_div->divide( i_obj1 = i_obj1
  439.             i_obj2 = i_obj2 ).
  440.     lr_div->multiply( i_obj1 = lr_div
  441.               i_obj2 = i_obj2 ).
  442.     subtract( i_obj1 = i_obj1
  443.           i_obj2 = lr_div ).
  444.   ENDMETHOD.                    “mod
  445.   METHOD power.
  446.     DATA: lr_power TYPE REF TO lcl_bignum,
  447.         lr_one   TYPE REF TO lcl_bignum.
  448.     CREATE OBJECT lr_one
  449.       EXPORTING
  450.         i_value = ‘1’.
  451.     CREATE OBJECT lr_power.
  452.     lr_power->copy_values( i_obj2 ).
  453.     lr_power->delete_leading_zeros( ).
  454. * only positive power
  455.     IF get_sign( ) NE lr_power->get_sign( ).
  456.       lr_power->invert_sign( ).
  457.     ENDIF.
  458. * result 1 when power is 0
  459.     set_data( ‘1’ ).
  460.     DO.
  461.       IF lr_power->get_int( ) IS INITIAL.
  462.         EXIT.
  463.       ELSE.
  464.         lr_power->subtract( i_obj1 = lr_power
  465.                   i_obj2 = lr_one ).
  466.         multiply( i_obj1 = me
  467.               i_obj2 = i_obj1 ).
  468.       ENDIF.
  469.     ENDDO.
  470.   ENDMETHOD.                    “power
  471. ENDCLASS.                    “lcl_bignum IMPLEMENTATION
  472. START-OF-SELECTION.
  473.   DATA lr1 TYPE REF TO lcl_bignum.
  474.   DATA lr2 TYPE REF TO lcl_bignum.
  475.   DATA lr3 TYPE REF TO lcl_bignum.
  476.   TRY .
  477.       CREATE OBJECT lr1
  478.         EXPORTING
  479.           i_value = ‘6584389034291301828999’.
  480. *          i_negative = ‘X’.
  481.       CREATE OBJECT lr2
  482.         EXPORTING
  483.           i_value = ‘533335555511111’.
  484.       CREATE OBJECT lr3.
  485.       lr3->add( i_obj1 = lr1
  486.           i_obj2 = lr2 ).
  487. *     lr3->subtract( i_obj1 = lr1
  488. *                    i_obj2 = lr2 ).
  489. *     lr3->multiply( i_obj1 = lr1
  490. *                    i_obj2 = lr2 ).
  491. *     lr3->divide( i_obj1 = lr1
  492. *                  i_obj2 = lr2 ).
  493. *     lr3->mod( i_obj1 = lr1
  494. *               i_obj2 = lr2 ).
  495.       lr1->set_data( ‘512’ ).
  496.       lr2->set_data( ’17’ ).
  497.       lr3->power( i_obj1 = lr1
  498.             i_obj2 = lr2 ).
  499.       lr2->set_data( ‘2773’ ).
  500.       lr3->mod( i_obj1 = lr3
  501.           i_obj2 = lr2 ).
  502.     CATCH cx_root.
  503.   ENDTRY.

/.

To report this post you need to login first.

6 Comments

You must be Logged on to comment or reply to a post.

  1. Suhas Saha

    Hello Manish,

    I am not sure on which version of SAP you are on; but if you are on releases > 702 you can use the decimal floating types(DECFLOAT) to get faster & more accurate results (ref – http://help.sap.com/abapdocu_740/en/abennews-71-decfloat.htm).

    For values beyond maximum limit of integer variable, workarounds can be used.

    I would rather use the proper type instead of using some workaround which is not full/fool proof. The choice on which numeric type to use can be made by keeping these points in mind – http://help.sap.com/abapdocu_740/en/abenselect_numeric_type_guidl.htm.

    I have used the type DECFLOAT34 to illustrate the calculations (using the numbers you have used in your example) –

    DATA:
           lv1     TYPE decfloat34,
           lv2     TYPE decfloat34,
           result  TYPE decfloat34.

    lv1 = ‘6584389034291301828999’.
    lv2 = ‘533335555511111’.

    *Add
    result = lv1 + lv2.
    WRITE: / `Addition:`,
               20 result NOGROUPING. CLEAR result.

    *Subtract
    result = lv1 lv2.
    WRITE: / `Subtraction:`,
               20 result NOGROUPING. CLEAR result.

    *Multiply
    result = lv1 * lv2.
    WRITE: / `Multiplication:`,
               20 result NOGROUPING. CLEAR result.

    *Divide
    result = lv1 / lv2.
    WRITE: / `Division:`,
               20 result NOGROUPING. CLEAR result.

    result = lv1 MOD lv2.
    WRITE: / `Modulo:`,
               20 result NOGROUPING. CLEAR result.

    Of course there is a limit to the accuracy using this data type as well, i would not argue that. But IMO, SAP is a business software & i’ll excuse it if it does not perform calculations with scientific accuracy ­čśŤ

    BR,

    Suhas

    (0) 
    1. Manish Kumar Post author

      Hi Suhas

      Thanks for the illustration. I am also on 702 release.

      My motive was to calculate the exact answer for ( 512 ^ 17 ) mod 2773.

      Decfloat34 gave 0 answer, and that is why program was written, purely as an academic exercise.

      (0) 
  2. Starlet Abraham

    Hi Manish,

    Q) Could you please tell me why this was used  in the constructor “split_into_int( )“.

    Googling for this I have received only replies pertaining to C#.

    Q) What is this kind of function called ? And are there others like this.

    Thanks.

    (0) 
    1. Manish Kumar Post author

      Hi Starlet

      Split_into_int( ) is a method of same local class lcl_bignum. Code is enclosed between Method Split_into_int and Endmethod statements.

      Since we are passing continuous string like ‘1234567890’ during Create Object, that string is broken down into internal table of integers using method split_into_int( ).

      After method call, table of integers would have 3 integers, 12(last entry), 3456, 7890 (first entry).

      It is a protected instance method of local class. Definition is kept in protected section because method is called only by other methods of same class, and not from outside.

      (0) 
  3. Harry Boeck

    While going through Project Euler, i found this class being very helpful at first. Later on, it showed a bunch of errors and limitations. Thus i rewrote it for elemination of errors, better performance, better usability and extended functionality.

    class Z04_BIGINT definition
      public
      create public .
    
    public section.
    *"* public components of class Z04_BIGINT
    *"* do not include other source files here!!!
    
      types DF type DECFLOAT34 .
      types DTYP type F .
      types:
        dtab type table of dtyp .
      types:
        tsign type c length 1 .
      types OBJREF type ref to Z04_BIGINT .
    
      class-data FZERO type DTYP read-only .
      class-data FHALF type DTYP read-only .
      class-data FMAXF type DTYP read-only .
      class-data ZERO type OBJREF read-only .
      class-data ONE type OBJREF read-only .
      class-data TWO type OBJREF read-only .
      class-data MAXINT type OBJREF read-only .
      class-data MAXINT_2 type OBJREF read-only .
      class-data FMANTSIZE type I read-only value 15. "#EC NOTEXT .
      class-data DFMANTSIZE type I read-only value 34. "#EC NOTEXT .
      class-data cdiv type i .
      class-data cdivloop type i .
      class-data cmul type i .
      class-data cmulloop type i .
      class-data cadd type i .
      class-data cget type i .
      class-data cset type i .
    
      methods CONSTRUCTOR .
      methods SETOBJ
        importing
          value(SRC) type OBJREF
        returning
          value(THIS) type OBJREF .
      methods SETF
        importing
          value(SRC) type F
          value(EXP) type I default 0
        returning
          value(THIS) type OBJREF .
      methods SETDF
        importing
          value(SRC) type DF
          value(EXP) type I default 0
        returning
          value(THIS) type OBJREF .
      methods SETI
        importing
          value(SRC) type I
        returning
          value(THIS) type OBJREF .
      methods SETSTR
        importing
          value(SRC) type STRING
        returning
          value(THIS) type OBJREF .
      methods GETSTR
        importing
          value(WITH_SIGN) type I default 1
        returning
          value(S) type STRING .
      methods GETF
        exporting
          value(MNT) type F
          value(EXP) type I .
      methods GETDF
        exporting
          value(MNT) type DF
          value(EXP) type I .
      methods GETSIGN
        returning
          value(S) type TSIGN .
      methods GETCIFCOUNT
        returning
          value(R) type I .
      methods ABS
        returning
          value(THIS) type OBJREF .
      methods NEG
        returning
          value(THIS) type OBJREF .
      methods IS_ZERO
        returning
          value(R) type I .
      methods ABSCMP
        importing
          !X type OBJREF
        returning
          value(R) type I .
      methods CMP
        importing
          !X type OBJREF
        returning
          value(R) type I .
      methods ADD
        importing
          !X type OBJREF
        returning
          value(THIS) type OBJREF .
      methods SUB
        importing
          !X type OBJREF
        returning
          value(THIS) type OBJREF .
      methods MUL
        importing
          !X type OBJREF
        returning
          value(THIS) type OBJREF .
      methods DIVX
        importing
          !X type OBJREF
        returning
          value(R) type OBJREF
        exceptions
          DIV_ZERO .
      methods DIV
        importing
          !X type OBJREF
          value(USEBUF) type I default 0
        returning
          value(THIS) type OBJREF
        exceptions
          DIV_ZERO .
      methods MOD
        importing
          !X type OBJREF
          value(USEBUF) type I default 0
        returning
          value(THIS) type OBJREF
        exceptions
          DIV_ZERO .
      methods POW
        importing
          !X type OBJREF
        returning
          value(THIS) type OBJREF
        exceptions
          NEGATIVE_EXPONENT
          TOO_BIG .
      methods POWMOD
        importing
          !X type OBJREF
          !M type OBJREF
        returning
          value(THIS) type OBJREF
        exceptions
          NEGATIVE_EXPONENT
          TOO_BIG .
      class-methods CLASS_CONSTRUCTOR .
      methods DEBUG .
    protected section.
    *"* protected components of class Z04_BIGINT
    *"* do not include other source files here!!!
    
      data SGN type I .
      data DAT type dtab .
      class-data CHUNKSIZE type I value 7. "#EC NOTEXT .
      class-data DCHUNKSIZE type I value 14. "#EC NOTEXT .
      class-data CHUNKMOD type dtyp value `1e7`. "#EC NOTEXT .
      class-data DCHUNKMOD type dtyp value `1e14`. "#EC NOTEXT .
      class-data O_D type OBJREF .
      class-data O_N type OBJREF .
      class-data O_Q type OBJREF .
      class-data O_R type OBJREF .
    
      class-methods STRPAD
        importing
          value(N) type I
          value(C) type C
          value(S) type STRING
        returning
          value(R) type STRING .
    private section.
    *"* private components of class Z04_BIGINT
    *"* do not include other source files here!!!
    ENDCLASS.
    
    
    
    CLASS Z04_BIGINT IMPLEMENTATION.
    
    
    * <SIGNATURE>---------------------------------------------------------------------------------------+
    * | Instance Public Method Z04_BIGINT->ABS
    * +-------------------------------------------------------------------------------------------------+
    * | [<-()] THIS                           TYPE        OBJREF
    * +--------------------------------------------------------------------------------------</SIGNATURE>
    method ABS.
      sgn = 1.
      this = me.
    endmethod.
    
    
    * <SIGNATURE>---------------------------------------------------------------------------------------+
    * | Instance Public Method Z04_BIGINT->ABSCMP
    * +-------------------------------------------------------------------------------------------------+
    * | [--->] X                              TYPE        OBJREF
    * | [<-()] R                              TYPE        I
    * +--------------------------------------------------------------------------------------</SIGNATURE>
    method ABSCMP.
    
      data: n1 type i,
            n2 type i,
            i  type i,
            v1 type dtyp,
            v2 type dtyp.
    
      n1 = lines(    dat ).
      n2 = lines( x->dat ).
    
      if ( n1 > n2 ).
        r = 1.
        return.
      endif.
      if ( n1 < n2 ).
        r = -1.
        return.
      endif.
    
      do n1 times.
        i = n1 - sy-index + 1.
        read table    dat into v1 index i.
        read table x->dat into v2 index i.
        if ( v1 > v2 ).
          r = 1.
          return.
        endif.
        if ( v1 < v2 ).
          r = -1.
          return.
        endif.
      enddo.
    
      r = 0.
    
    endmethod.
    
    
    * <SIGNATURE>---------------------------------------------------------------------------------------+
    * | Instance Public Method Z04_BIGINT->ADD
    * +-------------------------------------------------------------------------------------------------+
    * | [--->] X                              TYPE        OBJREF
    * | [<-()] THIS                           TYPE        OBJREF
    * +--------------------------------------------------------------------------------------</SIGNATURE>
    method ADD.
      " bei Unterschied im Vorzeichen auf Subtraktion wechseln;
      " dazu zwischendurch das Vorzeichen des Subtrahenden wechseln...
      if ( sgn <> x->sgn ).
        x->neg( ).
        sub( x ).
        x->neg( ).
        this = me.
        return.
      endif.
    
      data: n type i,
            nx type i,
            result type dtab,
            i type i,
            v1 type dtyp,
            v2 type dtyp,
            v3 type dtyp,
            ov type dtyp.
    
      n  = lines( dat ).
      nx = lines( x->dat ).
      if ( n < nx ). n = nx. endif.
    
      ov = 0.
      do n times.
        i = sy-index.
        " Das Lesen aus den Tabellen ist tolerant gegen├╝ber Wertebereichs├╝berschreitung des Index.
        " Aber in so einem Fall wird das Datenziel nicht genullt.
        clear: v1, v2.  " Nullsetzung auf k├╝rzestm├Âgliche Weise
        read table    dat into v1 index i.
        read table x->dat into v2 index i.
        v3 = v1 + v2 + ov.
        " Aufbereitungs-Variante mit div/mod:
        " => signifikant schneller als if/sub!
        " -> Diese Operationen werden offenbar in Hardware durchgef├╝hrt.
        " -> Sie sind deutlich schneller als elementare ABAP-Operationen.
        ov = v3 div chunkmod.
        v3 = v3 mod chunkmod.
        " Aufbereitungs-Variante mit Vergleich/sub:
    *    if ( v3 >= chunkmod ).
    *      ov = 1.
    *      v3 = v3 - chunkmod.
    *    else.
    *      ov = 0.
    *    endif.
        append v3 to result.
      enddo.
    
      if ( ov <> 0 ).
        append ov to result.
      endif.
    
      free dat.
      dat = result.
    
      this = me.
    endmethod.
    
    
    * <SIGNATURE>---------------------------------------------------------------------------------------+
    * | Static Public Method Z04_BIGINT=>CLASS_CONSTRUCTOR
    * +-------------------------------------------------------------------------------------------------+
    * +--------------------------------------------------------------------------------------</SIGNATURE>
    method CLASS_CONSTRUCTOR.
      fmaxf = '1.5e308'.
      fzero = 0.
      fhalf = '0.5'.
      create object: zero, one, two, maxint, o_d, o_n, o_r, o_q.", maxint_2
      zero->seti( 0 ).
      one->seti( 1 ).
      two->seti( 2 ).
      maxint->seti(   2 ** 31 - 1 ).
      "maxint_2->seti( 2 ** 30 - 1 ).
    endmethod.
    
    
    * <SIGNATURE>---------------------------------------------------------------------------------------+
    * | Instance Public Method Z04_BIGINT->CMP
    * +-------------------------------------------------------------------------------------------------+
    * | [--->] X                              TYPE        OBJREF
    * | [<-()] R                              TYPE        I
    * +--------------------------------------------------------------------------------------</SIGNATURE>
    method CMP.
    
      " Hier wird eine sophistische Vorzeichenbehandlung angesetzt,
      " die auch vorzeichenbehaftete Nullen einbezieht.
      " Wenn eine negative Zahl zu Null verkleinert wurde - etwa durch Division -,
      " wird diese als kleiner als eine positive zu Null verkleinerte Zahl betrachtet.
    
      data: iszero1 type i,
            iszero2 type i.
    
      iszero1 = is_zero( ).
      iszero2 = x->is_zero( ).
    
      if ( iszero1 + iszero2 = 2 ).
        r = 0.
        return.
      endif.
      if ( sgn = +1 and x->sgn = -1 ).
        r = +1.
        return.
      endif.
      if ( sgn = -1 and x->sgn = +1 ).
        r = -1.
        return.
      endif.
    
      data: cmp type i.
      cmp = abscmp( x ).
    
      if ( cmp > 0 ).
        if ( sgn = 1 ).
          r = +1.
        else.
          r = -1.
        endif.
      elseif ( cmp < 0 ).
        if ( sgn = 1 ).
          r = -1.
        else.
          r = +1.
        endif.
      else.
        r = 0.
      endif.
    
    endmethod.
    
    
    * <SIGNATURE>---------------------------------------------------------------------------------------+
    * | Instance Public Method Z04_BIGINT->CONSTRUCTOR
    * +-------------------------------------------------------------------------------------------------+
    * +--------------------------------------------------------------------------------------</SIGNATURE>
    method CONSTRUCTOR.
      sgn = 1.
      append fzero to dat.
    endmethod.
    
    
    * <SIGNATURE>---------------------------------------------------------------------------------------+
    * | Instance Public Method Z04_BIGINT->DEBUG
    * +-------------------------------------------------------------------------------------------------+
    * +--------------------------------------------------------------------------------------</SIGNATURE>
    method DEBUG.
      data: t type dtyp,
            cs type c.
    
      cs = getsign( ). write: / cs.
      loop at dat into t.
        write: / t.
      endloop.
    
    endmethod.
    
    
    * <SIGNATURE>---------------------------------------------------------------------------------------+
    * | Instance Public Method Z04_BIGINT->DIV
    * +-------------------------------------------------------------------------------------------------+
    * | [--->] X                              TYPE        OBJREF
    * | [--->] USEBUF                         TYPE        I (default =0)
    * | [<-()] THIS                           TYPE        OBJREF
    * | [EXC!] DIV_ZERO
    * +--------------------------------------------------------------------------------------</SIGNATURE>
    method DIV.
      if ( o_d->cmp( me ) = 0 and o_n->cmp( x ) = 0 ).
        me->setobj( o_q ).
        this = me.
        return.
      endif.
    
      o_d->setobj( me ).
      o_n->setobj( x ).
      o_r = divx( x ).
      o_q->setobj( me ).
    
      this = me.
    endmethod.
    
    
    * <SIGNATURE>---------------------------------------------------------------------------------------+
    * | Instance Public Method Z04_BIGINT->DIVX
    * +-------------------------------------------------------------------------------------------------+
    * | [--->] X                              TYPE        OBJREF
    * | [<-()] R                              TYPE        OBJREF
    * | [EXC!] DIV_ZERO
    * +--------------------------------------------------------------------------------------</SIGNATURE>
    method DIVX.
      data: z   type objref,  " Zwischenergebnis eines N├Ąherungsschritts
            mr  type dtyp,  " Mantisse Rest
            er  type i,     " Exponent Rest
            mx  type dtyp,  " Mantisse Nenner (X)
            ex  type i,     " Exponent Nenner (X)
            mv  type dtyp,  " Mantisse Verh├Ąltnis
            ev  type i,     " Exponent Verh├Ąltnis
            eva type i,     " Verh├Ąltnis-Exponent, der direkt als Nullen eingesetzt wird
            evb type i.     " Verh├Ąltnis-Exponent, der ├╝ber setf eingesetzt wird
    
      cget = 0.
      cset = 0.
      cadd = 0.
      cmul = 0.
      cmulloop = 0.
      cdivloop = 0.
    
      create object: r, z.
    
      r->setobj( me ).
      me->setobj( zero ).
    
      x->GETF( IMPORTING MNT = mx EXP = ex ).
    
      cget = cget + 1.
    
      while ( r->abscmp( x ) >= 0 ).
    
        r->GETF( IMPORTING MNT = mr EXP = er ).
        " Das im folgenden ermittelte Verh├Ąltnis ist eine Sch├Ątzung auf etwa 15 Stellen Genauigkeit.
        " Die kann etwas zu klein, aber auch etwas zu gro├č ausfallen.
        " Dies ist kein Problem: Der Rest darf zwischendurch ruhig das Vorzeichen wechseln,
        " er wird trotzdem immer in Richtung kleiner werdender Betr├Ąge abgebaut.
        " Wenn wir am Punkt angekommen sind, dass der Rest-Betrag kleiner als x geworden ist,
        " m├╝ssen wir nochmal testen, ob der Rest das selbe Vorzeichen hat wie zu Anfang
        " (was identisch ist zum Produkt der Vorzeichen von Quotient und Nenner).
        " Falls nicht, muss der Quotient um ein 1 und der Rest um x korrigiert werden.
    
        mv = mr / mx.
        ev = er - ex.
        z->setf( src = mv exp = ev ).
        me->add( z ).
        r->sub( z->mul( x ) ).
    
        cdivloop = cdivloop + 1.
        cget = cget + 1.
        cset = cset + 1.
        cadd = cadd + 2.
        cmul = cmul + 1.
    
      endwhile.
    
      " Rest-Underflow?
      " -> Korrektur des Quotienten um -1 relativ zu seinem Vorzeichen
      "     und des Rests um jene Quotientenkorrektur mal -x
      " (Um die Faktoren besser lesbar auseinanderzuhalten, wird das aktuelle Objekt,
      " welches den Quotienten darstellt, explizit mit "me->" gekennzeichnet.)
      if ( r->sgn <> me->sgn * x->sgn ).
        z->seti( me->sgn ).
        me->sub( z ).
        r->add( z->mul( x ) ).
    
        cset = cset + 1.
        cadd = cadd + 2.
        cmul = cmul + 1.
      endif.
    
    *  write: / 'divx get count:',cget.
    *  write: / 'divx set count:',cset.
    *  write: / 'divx add count:',cadd.
    *  write: / 'divx mul count:',cmul.
    *  write: / 'divx mulloop count:',cmulloop.
    *  write: / 'divx divloop count:',cdivloop.
    
      free z.
    
    endmethod.
    
    
    * <SIGNATURE>---------------------------------------------------------------------------------------+
    * | Instance Public Method Z04_BIGINT->GETCIFCOUNT
    * +-------------------------------------------------------------------------------------------------+
    * | [<-()] R                              TYPE        I
    * +--------------------------------------------------------------------------------------</SIGNATURE>
    method GETCIFCOUNT.
      data: n type i,
            v type dtyp,
            s type string.
    
      n = lines( dat ).
      r = ( n - 1 ) * chunksize.
      read table dat into v index n.
      r = r + ceil( log10( v + fhalf ) ).
    endmethod.
    
    
    * <SIGNATURE>---------------------------------------------------------------------------------------+
    * | Instance Public Method Z04_BIGINT->GETDF
    * +-------------------------------------------------------------------------------------------------+
    * | [<---] MNT                            TYPE        DF
    * | [<---] EXP                            TYPE        I
    * +--------------------------------------------------------------------------------------</SIGNATURE>
    method GETDF.
      data: i     type i,
            imin  type i,
            v     type dtyp.
    
      mnt = 0.
      exp = 0.
      i = lines( dat ).
      imin = nmax( val1 = 1 val2 = i - 5 ).
      while ( i >= imin ).
        read table dat into v index i.
        mnt = mnt * chunkmod + v.
        i = i - 1.
      endwhile.
      if ( imin > 1 ).
        exp = i * chunksize.
      endif.
      mnt = mnt * sgn.
    endmethod.
    
    
    * <SIGNATURE>---------------------------------------------------------------------------------------+
    * | Instance Public Method Z04_BIGINT->GETF
    * +-------------------------------------------------------------------------------------------------+
    * | [<---] MNT                            TYPE        F
    * | [<---] EXP                            TYPE        I
    * +--------------------------------------------------------------------------------------</SIGNATURE>
    method GETF.
      data: i     type i,
            imin  type i,
            v     type dtyp.
    
      mnt = 0.
      exp = 0.
      i = lines( dat ).
      imin = nmax( val1 = 1 val2 = i - 2 ).
      while ( i >= imin ).
        read table dat into v index i.
        mnt = mnt * chunkmod + v.
        i = i - 1.
      endwhile.
      if ( imin > 1 ).
        exp = i * chunksize.
      endif.
      mnt = mnt * sgn.
    endmethod.
    
    
    * <SIGNATURE>---------------------------------------------------------------------------------------+
    * | Instance Public Method Z04_BIGINT->GETSIGN
    * +-------------------------------------------------------------------------------------------------+
    * | [<-()] S                              TYPE        TSIGN
    * +--------------------------------------------------------------------------------------</SIGNATURE>
    method GETSIGN.
      if ( sgn = 1 ).
        s = '+'.
      else.
        s = '-'.
      endif.
    endmethod.
    
    
    * <SIGNATURE>---------------------------------------------------------------------------------------+
    * | Instance Public Method Z04_BIGINT->GETSTR
    * +-------------------------------------------------------------------------------------------------+
    * | [--->] WITH_SIGN                      TYPE        I (default =1)
    * | [<-()] S                              TYPE        STRING
    * +--------------------------------------------------------------------------------------</SIGNATURE>
    method GETSTR.
      data: v type dtyp,
            st type string,
            n type i.
    
      n = lines( dat ).
      loop at dat into v.
        st = |{ v }|.
        "write: / ' getstr:',st.
        if ( sy-tabix < n ).
          s = strpad( s = st n = chunksize c = '0' ) && s.
        else.
          s = st && s.
        endif.
      endloop.
    
      if ( with_sign = 1 ).
        s = getsign( ) && s.
      endif.
    
    endmethod.
    
    
    * <SIGNATURE>---------------------------------------------------------------------------------------+
    * | Instance Public Method Z04_BIGINT->IS_ZERO
    * +-------------------------------------------------------------------------------------------------+
    * | [<-()] R                              TYPE        I
    * +--------------------------------------------------------------------------------------</SIGNATURE>
    method IS_ZERO.
    
      if ( lines( dat ) > 1 ).
        r = 0.
        return.
      endif.
    
      data d type dtyp.
    
      read table dat into d index 1.
      if ( d > 0 ).
        r = 0.
      else.
        r = 1.
      endif.
    
    endmethod.
    
    
    * <SIGNATURE>---------------------------------------------------------------------------------------+
    * | Instance Public Method Z04_BIGINT->MOD
    * +-------------------------------------------------------------------------------------------------+
    * | [--->] X                              TYPE        OBJREF
    * | [--->] USEBUF                         TYPE        I (default =0)
    * | [<-()] THIS                           TYPE        OBJREF
    * | [EXC!] DIV_ZERO
    * +--------------------------------------------------------------------------------------</SIGNATURE>
    method MOD.
      if ( o_d->cmp( me ) = 0 and o_n->cmp( x ) = 0 ).
        me->setobj( o_r ).
        this = me.
        return.
      endif.
    
      o_d->setobj( me ).
      o_n->setobj( x ).
      o_r = divx( x ).
      o_q->setobj( me ).
      me->setobj( o_r ).
    
      this = me.
    endmethod.
    
    
    * <SIGNATURE>---------------------------------------------------------------------------------------+
    * | Instance Public Method Z04_BIGINT->MUL
    * +-------------------------------------------------------------------------------------------------+
    * | [--->] X                              TYPE        OBJREF
    * | [<-()] THIS                           TYPE        OBJREF
    * +--------------------------------------------------------------------------------------</SIGNATURE>
    method MUL.
      sgn = sgn * x->sgn.
    
      data: n1 type i,
            n2 type i,
            result type dtab,
            i1 type i,
            i2 type i,
            i3 type i,
            i1min type i,
            i3max type i,
            v1 type dtyp,
            v2 type dtyp,
            v3 type dtyp,
            ov type dtyp,
            vr type dtyp.
    
      if ( is_zero( ) + x->is_zero( ) = 1 ).
        clear dat.
        append fzero to dat.
        this = me.
        return.
      endif.
    
      n1 = lines( dat ).
      n2 = lines( x->dat ).
    
      " Addition nach Zielstellen...
      " i3 gibt die Zielstelle an.
      " Es sind alle i1 + i2 = i3 zu durchlaufen!
      " Nullbasiert w├Ąre das:
      "   i1 = i1min...i3   i1min = max( 0, i3 - n2 )
      "   i2 = i3 - i1
      " Das bei SAP 1-basiert indiziert wird,
      " muss an den richtigen Stellen noch +1 erg├Ąnzt werden!
      "
      " Es ist zu bedenken, dass die Menge der bearbeiteten chunks SEHR gro├č sein kann!
      " Das resultiert in gro├čem Wertebereich f├╝r den Overflow!
      " Der ist halt nicht blo├č eine eventuelle "1", sondern nimmt maximal den Wert
      " der Menge der beteiligten chunks an!
      " Wir haben bei den Zwischen-Additionen also gro├če Zahlen
      " aus drei chunks zu verarbeiten.
      " Die chunksize ist so bemessen, dass das Ergebnis einer Multiplikation
      " plus eine ├ťberlaufstelle im zugrundeliegenden chunk-Datentyp aufgenommen werden kann.
      i3max = ( n1 + n2 - 1 ).
      i3 = 1.
      do i3max times.
    
        i1min = nmax( val1 = 1 val2 = i3 - n2 + 1 ).
        i1 = i1min.
        i2 = i3 - i1 + 1.
    
        while ( i1 <= n1 and i2 > 0 ).
          " Da wir hier explizit nur Elemente durchgehen, die tats├Ąchlich existieren,
          " werden alle read tables erfolgreich durchgehen.
          " Das Nullen der Zielwerte k├Ânnen wir uns deshalb ersparen.
          read table    dat into v1 index i1.
          read table x->dat into v2 index i2.
    
          "data s  type string.
          "s = `  1: ` && v1 && `[` && i1 && `]  2: ` && v2 && `[` && i2 && `]`.
          "write: / s.
    
          v3 = v3 + v1 * v2.
          ov = ov + v3 div dchunkmod.
          v3 =      v3 mod dchunkmod.
          i1 = i1 + 1.
          i2 = i2 - 1.
    
          cmulloop = cmulloop + 1.
    
        endwhile.
    
        vr = v3 mod chunkmod.
        v3 = v3 div chunkmod + ov * chunkmod.
        ov = fzero.
        append vr to result.
    
        i3 = i3 + 1.
      enddo.
      " Im Ergebnis wird es nie mehr Stellen geben als in den letzten v3 gepasst haben.
      " v3 wird im letzten Schleifendurchlauf innerhalb chunkmod bleiben.
    
      if ( v3 <> 0 ).
        append v3 to result.
      endif.
    
      free dat.
      dat = result.
    
      this = me.
    endmethod.
    
    
    * <SIGNATURE>---------------------------------------------------------------------------------------+
    * | Instance Public Method Z04_BIGINT->NEG
    * +-------------------------------------------------------------------------------------------------+
    * | [<-()] THIS                           TYPE        OBJREF
    * +--------------------------------------------------------------------------------------</SIGNATURE>
    method NEG.
      sgn = - sgn.
      this = me.
    endmethod.
    
    
    * <SIGNATURE>---------------------------------------------------------------------------------------+
    * | Instance Public Method Z04_BIGINT->POW
    * +-------------------------------------------------------------------------------------------------+
    * | [--->] X                              TYPE        OBJREF
    * | [<-()] THIS                           TYPE        OBJREF
    * | [EXC!] NEGATIVE_EXPONENT
    * | [EXC!] TOO_BIG
    * +--------------------------------------------------------------------------------------</SIGNATURE>
    method POW.
      if ( x->cmp( zero ) < 0 ).
        raise NEGATIVE_EXPONENT.
      endif.
    
      " Wir haben einen Ziel-Speicherbedarf von: Originalgr├Â├če * x.
      " Zwischendurch haben wir denselben Speicherbedarf f├╝r Zwischenwerte nochmal.
      " Wobei nicht dokumentiert ist, wieviele Elemente ein Array alias interne Tabelle
      " halten kann. Denkbar ist ohne weiteres, dass dies auf 64-bit-Systemen ausschlie├člich
      " durch die Adressierbarkeit der Array-Bl├Âcke und deren Begrenzung durch den
      " Integer-Wertebereich der Indizes gegeben ist.
      " Au├čerdem ist nicht dokumentiert, wieviel RAM eine Session verwalten kann.
      " Ich setze hier also erstmal an, dass ich - falls nicht ein Speicher├╝berlauf
      " vom Betriebssystem kommt - bis zu maxint Bl├Âcke adressieren k├Ânnen sollte.
      "
      " Letztlich k├Ânnen wir hier eh nur eine Absch├Ątzung geben.
      " Ob und wann eine Exception kommt, entscheidet die Laufzeitumgebung und die
      " konkrete F├╝llung der Argumente.
      " => Die folgende sehr grobe Sch├Ątzung wurde wieder deaktiviert.
      "
      "is_too_big = x->cmp( maxint_2 ).
      "if ( is_too_big > 0 ).
      "  raise TOO_BIG.
      "endif.
    
      data: cifcount type i,
            is_too_big type i,
            exptmp  type i,
            expmax  type i,
            exp     type i,
            ei      type i,
            facs    type table of objref,
            fac     type objref,
            factmp  type objref.
    
      try.
        "------------------------
        " Potenz-Faktoren anlegen
        "------------------------
        create object factmp.
        factmp->setobj( me ).
    
        exptmp = 1.
        exp = x->getstr( ).
        expmax = exp div 2.
        "write: / '  exp:',exp,', expmax:',expmax.
    
        create object fac.
        fac->setobj( factmp ).
        append fac to facs.
    
        while ( exptmp <= expmax ).
          create object fac.
          fac->setobj( factmp ).
          fac->mul( fac ).
          exptmp = exptmp + exptmp.
          append fac to facs.
          factmp->setobj( fac ).
    
          "data s type string.
          "s = fac->getstr( ).
          "write: / '  faktor:',s.
    
        endwhile.
    
        "loop at facs into fac.
        "  s = '  facs[' && sy-tabix && ']:' && fac->getstr( ).
        "  write: / s.
        "endloop.
    
        "------------------------
        " Potenz bilden
        "------------------------
        factmp->seti( 1 ).
        "exptmp wird aus der Init-Schleife ├╝bernommen
        ei = lines( facs ).
        while ( exp > 0 ).
          read table facs into fac index ei.
          if ( exptmp <= exp ).
            factmp->mul( fac ).
    
            "s = '  fac:' && fac->getstr( ) && ', exptmp:' && exptmp && ', exp:' && exp.
            "write: / s.
    
            exp = exp - exptmp.
          endif.
          exptmp = exptmp div 2.
          ei = ei - 1.
        endwhile.
    
        setobj( factmp ).
        loop at facs into fac.
          free fac.
        endloop.
        free factmp.
    
      catch cx_root.
        if ( not factmp is initial ).
          loop at facs into fac.
            free fac.
          endloop.
          free factmp.
        endif.
        raise TOO_BIG.
      endtry.
    
      this = me.
    endmethod.
    
    
    * <SIGNATURE>---------------------------------------------------------------------------------------+
    * | Instance Public Method Z04_BIGINT->POWMOD
    * +-------------------------------------------------------------------------------------------------+
    * | [--->] X                              TYPE        OBJREF
    * | [--->] M                              TYPE        OBJREF
    * | [<-()] THIS                           TYPE        OBJREF
    * | [EXC!] NEGATIVE_EXPONENT
    * | [EXC!] TOO_BIG
    * +--------------------------------------------------------------------------------------</SIGNATURE>
    method POWMOD.
      " Restklassen-Rechnung erlaubt, den numerischen Aufwand
      " bei Multiplikation und Potenzierung krass zu reduzieren.
      "   .
      " - Multiplikation einer Zahl mit einem beliebigen Faktor modulo m:
      "       z mod m = (    x * m +     r) mod m =     r
      "   k * z mod m = (k * x * m + k * r) mod m = k * r
      "   .
      " - auch der Faktor kann modulo m reduziert werden:
      "       a = xa * m + ra
      "       b = xb * m + rb
      "   a * b mod m = (xa*xb * m^2 + (xa*rb + xb*ra) * m + ra*rb) mod m = ra*rb
      "   .
      " -> Da in einer Restklassenbetrachtung das k*x*m v├Âllig wurscht ist,
      " kann man sich, solange man nur mit ganzen Zahlen k rechnet, das k*x*m klemmen.
      " In jedem Multiplikations-Rechenschritt. Indem man stets auf die Restklasse reduziert.
      " .
      " Das machen wir hier.
      " Dadurch fallen alle Beschr├Ąnkungen des Exponenten.
      " Die Prozedur ist kopiert vom normalen pow.
    
      data: cmp type i.
    
      cmp = x->cmp( zero ).
      if ( cmp < 0 ).
        raise NEGATIVE_EXPONENT.
      endif.
    
      " Extrabehandlung des Falls x = 0 (passt nicht in die Logik weiter unten)
      if ( cmp = 0 ).
        me->setobj( one ).
        this = me.
        return.
      endif.
    
      data: cifcount type i,
            is_too_big type i,
            exp     type objref,
            exptmp  type objref,
            en      type i,
            ei      type i,
            facs    type table of objref,
            exps    type table of objref,
            fac     type objref.
    
      try.
        "------------------------
        " Potenz-Faktoren anlegen
        "------------------------
        " Die Zweierpotenzreihe zu speichern ist effektiver als sie
        " beim Abw├Ąrtswandern der Faktorenliste auszudividieren.
        create object: exptmp.
    
        exptmp->setobj( one ). " me = original me ^ 1
        o_r = me->divx( m ). me->setobj( o_r ). " Reduzierung auf Restklasse
    
        while ( exptmp->cmp( x ) <= 0 ).
          create object fac. fac->setobj( me ).
          create object exp. exp->setobj( exptmp ).
          append fac to facs.
          append exp to exps.
          me->mul( me ).          " me = me^2 = original me ^ exp
          exptmp->add( exptmp ).  " exp = 2 ^ schleifenindex
          o_r = me->divx( m ). me->setobj( o_r ). " Reduzierung auf Restklasse
        endwhile.
    
        " debugging...
        "data s type string.
        "ei = lines( facs ).
        "if ( ei > 0 ).
        "  write: / 'potenz-faktoren:'.
        "  while ( ei > 0 ).
        "    read table facs into fac index ei.
        "    read table exps into exp index ei.
        "    s = exp->getstr( ) && ' -> ' && fac->getstr( ).
        "    write: / s.
        "    ei = ei - 1.
        "  endwhile.
        "endif.
    
        "------------------------
        " Potenz bilden
        "------------------------
        me->setobj( one ).
        exptmp->setobj( x ).
        en = lines( facs ).
        ei = en.
        do en times.
          if ( exptmp->cmp( zero ) <= 0 ).
            exit.
          endif.
          read table facs into fac index ei.
          read table exps into exp index ei.
          cmp = exp->cmp( exptmp ).
          if ( cmp <= 0 ).
            me->mul( fac ).
            exptmp->sub( exp ).
            o_r = me->divx( m ). me->setobj( o_r ). " Reduzierung auf Restklasse
            if ( cmp = 0 ).
              exit.
            endif.
          endif.
          ei = ei - 1.
        enddo.
    
        loop at facs into fac. free fac. endloop.
        loop at exps into exp. free exp. endloop.
        free exptmp.
    
      catch cx_root.
        if ( not exptmp is initial ). free exptmp. endif.
        loop at facs into fac. free fac. endloop.
        loop at exps into exp. free exp. endloop.
        raise TOO_BIG.
      endtry.
    
      this = me.
    endmethod.
    
    
    * <SIGNATURE>---------------------------------------------------------------------------------------+
    * | Instance Public Method Z04_BIGINT->SETDF
    * +-------------------------------------------------------------------------------------------------+
    * | [--->] SRC                            TYPE        DF
    * | [--->] EXP                            TYPE        I (default =0)
    * | [<-()] THIS                           TYPE        OBJREF
    * +--------------------------------------------------------------------------------------</SIGNATURE>
    method SETDF.
      data: val   type df,
            rest  type df,
            t     type df,
            xexp  type i, " Zehnerpotenz des Quellwertes
            texp  type i, " Zehnerpotenz-├ťbertrag in/aus Nullenkette
            mexp  type i,
            nzch  type i, " Anzahl der abschlie├čend zu erg├Ąnzenden Null-Chunks
            izch  type i.
    
      if ( src >= 0 ). sgn = +1. val =   src.
      else.            sgn = -1. val = - src.
      endif.
    
      " Der reinkommende float wird dezimal so zurechtger├╝ckt,
      " dass er nach M├Âglichkeit gerade genau dfmantsize Ziffern enth├Ąlt.
      " wobei die kleinste Ziffer den Stellenwert 1 haben soll.
      " Dazu wird die Zehnerpotenz korrigiert, indem ├╝berfl├╝ssige Zehnerpotenzen
      " nach "exp" verschoben werden bzw. fehlende aus diesem abgezogen.
      " Nebenbei wird daf├╝r gesorgt, dass die in "exp" verbleibenden Zehnerpotenzen
      " ein ganzzahliges Vielfaches der chunksize sind, so dass diese Nullen
      " geradlinig schlicht als Null-Chunks angeh├Ąngt werden k├Ânnen.
      "-----------------
      "xexp = ceil( log10( val ) ).
      " Die Funktion log10 ist f├╝r decfloats EXTREM aufwendig implementiert
      " (rund 500 mikrosec -> das mehr als 1000-fache des Wertes f├╝r float).
      " div (und powmod als abh├Ąngige Funktion) werden dadurch extrem ausgebremst.
      xexp = CL_ABAP_MATH=>GET_NUMBER_OF_DIGITS( floor( val ) ).
    
      texp = xexp - dfmantsize. " durch 10 ** texp wird val nachher dividiert
      exp = exp + texp.         " ...und diese Nullen sp├Ąter direkt angeh├Ąngt
      if ( exp < 0 ).
        texp = texp - exp.
        exp = 0.
      endif.
      mexp = exp mod chunksize.
      nzch = exp div chunksize.
      texp = texp - mexp.
    
      "write: / '  aus Mantisse entfernt:',texp,', Anzahl Nullchunks:',nzch.
    
      " Zehnerpotenz-Korrektur und Rundung (auf h├Âchstens runter bis Stellenwert 1)
      val = round( val = val / ( 10 ** texp ) dec = nmin( val1 = 0 val2 = - ( xexp - texp - dfmantsize ) ) ) .
    
      clear dat.
      if ( nzch > 0 ).
        append fzero to dat.
        izch = 1.
        nzch = nzch - izch.
        while ( nzch >= izch ).
          append lines of dat from 1 to izch to dat.
          nzch = nzch - izch.
          izch = izch + izch.
        endwhile.
        if ( nzch > 0 ).
          append lines of dat from 1 to nzch to dat.
        endif.
      endif.
    
      while ( val >= chunkmod ).
        "rest = val mod chunkmod.
        "val  = val div chunkmod.
        t = val div chunkmod.
        rest = val - t * chunkmod.
        val = t.
    
        append rest to dat.
      endwhile.
      append val to dat.
    
      this = me.
    endmethod.
    
    
    * <SIGNATURE>---------------------------------------------------------------------------------------+
    * | Instance Public Method Z04_BIGINT->SETF
    * +-------------------------------------------------------------------------------------------------+
    * | [--->] SRC                            TYPE        F
    * | [--->] EXP                            TYPE        I (default =0)
    * | [<-()] THIS                           TYPE        OBJREF
    * +--------------------------------------------------------------------------------------</SIGNATURE>
    method SETF.
      data: val   type f,
            rest  type f,
            xexp  type i, " Zehnerpotenz des Quellwertes
            texp  type i, " Zehnerpotenz-├ťbertrag in/aus Nullenkette
            mexp  type i,
            nzch  type i, " Anzahl der abschlie├čend zu erg├Ąnzenden Null-Chunks
            izch  type i.
    
      if ( src >= 0 ). sgn = +1. val =   src.
      else.            sgn = -1. val = - src.
      endif.
    
      " Der reinkommende float wird dezimal so zurechtger├╝ckt,
      " dass er nach M├Âglichkeit gerade genau fmantsize Ziffern enth├Ąlt.
      " wobei die kleinste Ziffer den Stellenwert 1 haben soll.
      " Dazu wird die Zehnerpotenz korrigiert, indem ├╝berfl├╝ssige Zehnerpotenzen
      " nach "exp" verschoben werden bzw. fehlende aus diesem abgezogen.
      " Nebenbei wird daf├╝r gesorgt, dass die in "exp" verbleibenden Zehnerpotenzen
      " ein ganzzahliges Vielfaches der chunksize sind, so dass diese Nullen
      " geradlinig schlicht als Null-Chunks angeh├Ąngt werden k├Ânnen.
      xexp = ceil( log10( val ) ).
      texp = xexp - fmantsize.  " durch 10 ** texp wird val nachher dividiert
      exp = exp + texp.         " ...und diese Nullen sp├Ąter direkt angeh├Ąngt
      if ( exp < 0 ).
        texp = texp - exp.
        exp = 0.
      endif.
      mexp = exp mod chunksize.
      nzch = exp div chunksize.
      texp = texp - mexp.
    
      "write: / '  aus Mantisse entfernt:',texp,', Anzahl Nullchunks:',nzch.
    
      " Zehnerpotenz-Korrektur und Rundung (auf h├Âchstens runter bis Stellenwert 1)
      val = round( val = val / ( 10 ** texp ) dec = nmin( val1 = 0 val2 = - ( xexp - texp - fmantsize ) ) ) .
    
      clear dat.
      if ( nzch > 0 ).
        append fzero to dat.
        izch = 1.
        nzch = nzch - izch.
        while ( nzch >= izch ).
          append lines of dat from 1 to izch to dat.
          nzch = nzch - izch.
          izch = izch + izch.
        endwhile.
        if ( nzch > 0 ).
          append lines of dat from 1 to nzch to dat.
        endif.
      endif.
    
      while ( val >= chunkmod ).
        rest = val mod chunkmod.
        "val  = val div chunkmod.
        " Die Operation "div" ist EIGENTLICH das zu "mod" passende Komplement.
        " Sie liefert allerdings Nachkommastellen, wenn die Zahl urspr├╝nglich Nachkommastellen enth├Ąlt
        " (so, dass (x div y)*y+(x mod y) == x).
        " Eigentlich kommt val hier gerundet rein, aber beim Dividieren durch chunkmod
        " k├Ânnen Rundungsfehler auftreten.
        " mod arbeitet aber nur wie gebraucht, wenn eben KEINE Nachkommastellen vorhanden sind.
        " Deshalb wird hier die aufwendigere Form mit floor gew├Ąhlt.
        val = floor( val / chunkmod ).
        append rest to dat.
      endwhile.
      append val to dat.
    
      this = me.
    endmethod.
    
    
    * <SIGNATURE>---------------------------------------------------------------------------------------+
    * | Instance Public Method Z04_BIGINT->SETI
    * +-------------------------------------------------------------------------------------------------+
    * | [--->] SRC                            TYPE        I
    * | [<-()] THIS                           TYPE        OBJREF
    * +--------------------------------------------------------------------------------------</SIGNATURE>
    method SETI.
      clear dat.
    
      data: val type f,
            rest type f.
    
      if ( src >= 0 ). sgn = +1. val =   src.
      else.            sgn = -1. val = - src.
      endif.
    
      if ( val >= chunkmod ).
        rest = val mod chunkmod.
        append rest to dat.
        val = val div chunkmod.
      endif.
      append val to dat.
    
      this = me.
    endmethod.
    
    
    * <SIGNATURE>---------------------------------------------------------------------------------------+
    * | Instance Public Method Z04_BIGINT->SETOBJ
    * +-------------------------------------------------------------------------------------------------+
    * | [--->] SRC                            TYPE        OBJREF
    * | [<-()] THIS                           TYPE        OBJREF
    * +--------------------------------------------------------------------------------------</SIGNATURE>
    method SETOBJ.
      dat = src->dat.
      sgn   = src->sgn.
      this = me.
    endmethod.
    
    
    * <SIGNATURE>---------------------------------------------------------------------------------------+
    * | Instance Public Method Z04_BIGINT->SETSTR
    * +-------------------------------------------------------------------------------------------------+
    * | [--->] SRC                            TYPE        STRING
    * | [<-()] THIS                           TYPE        OBJREF
    * +--------------------------------------------------------------------------------------</SIGNATURE>
    method SETSTR.
      clear dat.
    
      data: ofs type i,
            mpart type string,
            xpart type string,
            ipart type string,
            fpart type string,
            chunk type f,
            ilen type i,
            flen type i,
            sc type c,
            x type i.
    
      "write: / '  originale src:', src.
    
      " Entschrottung, Vereinfachung und Zerlegung
      "-----------------------
      condense src.
      translate src to lower case.
        " to do: Pr├╝fung mit regex
      split src   at 'e' into mpart xpart.
      split mpart at '.' into ipart fpart.
      ilen = strlen( ipart ).
      flen = strlen( fpart ).
    
      "write: / '  parts:', ipart,',', fpart,' * 10^ ', xpart.
    
      " Vorzeichen-Erkennung
      "-----------------------
      sgn = +1.
      if ( ilen > 0 ).
        sc = ipart(1).
        if ( sc = '-' or sc = '+' ).
          if ( sc = '-' ). sgn = -1. endif.
          ipart = ipart+1.
          ilen = ilen - 1.
        endif.
      endif.
      "write: / '  ipart nach Vorzeichenerkennung:', ipart.
    
      " Zehnerpotenz-Erkennung
      "-----------------------
      x = xpart.
      "write: / '  Zehnerpotenz / originale L├Ąnge: ',x,ilen.
      ilen = ilen + x.
      "write: / '  Zehnerpotenz / korrigierte L├Ąnge: ',ilen.
      if ( x < 0 ).
        if ( ilen < 1 ).
          ilen = 1.
          ipart = '0'.
        endif.
        "write: / '  negative Zehnerpotenz -> verbleibende L├Ąnge: ',ilen.
        ipart = ipart(ilen).
      elseif ( x > 0 ).
        if ( x < flen ).
          ipart = ipart && fpart(x).
        else.
          x = x - flen.
          ipart = ipart && fpart && repeat( val = '0' occ = x ).
        endif.
      endif.
      "write: / '  ipart nach Zehnerpotenz-Erkennung:', ipart.
    
      " Integer-Zerlegung
      "-----------------------
      while ( ilen > 0 ).
        ilen = ilen - chunksize.
        if ( ilen < 0 ).
          ilen = ilen + chunksize.
          chunk = ipart(ilen).
          append chunk to dat.
          exit.
        endif.
        chunk = ipart+ilen(chunksize).
        append chunk to dat.
      endwhile.
    
      this = me.
    
    endmethod.
    
    
    * <SIGNATURE>---------------------------------------------------------------------------------------+
    * | Static Protected Method Z04_BIGINT=>STRPAD
    * +-------------------------------------------------------------------------------------------------+
    * | [--->] N                              TYPE        I
    * | [--->] C                              TYPE        C
    * | [--->] S                              TYPE        STRING
    * | [<-()] R                              TYPE        STRING
    * +--------------------------------------------------------------------------------------</SIGNATURE>
    method STRPAD.
      data: d type i.
      d = n - strlen( s ).
      if ( d < 0 ). r = s. return. endif.
    
      " repeat ist 10% schneller als das Ausschneiden aus einem vorbereiteten String...
      r = repeat( val = '0' occ = d ) && s.
    endmethod.
    
    
    * <SIGNATURE>---------------------------------------------------------------------------------------+
    * | Instance Public Method Z04_BIGINT->SUB
    * +-------------------------------------------------------------------------------------------------+
    * | [--->] X                              TYPE        OBJREF
    * | [<-()] THIS                           TYPE        OBJREF
    * +--------------------------------------------------------------------------------------</SIGNATURE>
    method SUB.
      " bei Unterschied im Vorzeichen auf Addition wechseln;
      " dazu zwischendurch das Vorzeichen des Subtrahenden wechseln...
      if ( sgn <> x->sgn ).
        x->neg( ).
        add( x ).
        x->neg( ).
        this = me.
        return.
      endif.
    
      " Den gr├Â├čeren Wert vom kleineren Wert abziehen
      " (Alternative w├Ąre blind modulo zu subtrahieren und zum Schluss
      " bei verbleibendem ├ťberlauf das Ergebnis nochmal komplett zu negieren.
      " Das ist allerdings mit mehr Aufwand verbunden als beim Tausch vorweg.)
      data: cmp type i.
    
      field-symbols:
        <d1> type dtab,
        <d2> type dtab.
    
      " ...Dazu den Fall Null extra behandeln, weil der recht h├Ąufig vorkommen kann
      "   und besonders fix ├╝ber die B├╝hne zu bringen ist...
      cmp = abscmp( x ).
      if ( cmp = 0 ).
        clear dat.
        append fzero to dat.
        this = me.
        return.
      endif.
    
      if ( cmp < 0 ).
        assign dat    to <d2>.
        assign x->dat to <d1>.
        neg( ).
      else.
        assign dat    to <d1>.
        assign x->dat to <d2>.
      endif.
    
      " Letztlich die eigentliche Subtraktion durchf├╝hren
      data: n type i,
            result type dtab,
            i type i,
            v1 type dtyp,
            v2 type dtyp,
            v3 type dtyp,
            ov type dtyp.
    
      n  = lines( <d1> ).
    
      " ov = 0. " ├╝berfl├╝ssig
      do n times.
        i = sy-index.
        " Das Lesen aus den Tabellen ist tolerant gegen├╝ber Wertebereichs├╝berschreitung des Index.
        " Aber in so einem Fall wird das Datenziel nicht genullt.
        clear: v1, v2.  " Nullsetzung auf k├╝rzestm├Âgliche Weise
        read table <d1> into v1 index i.
        read table <d2> into v2 index i.
        v3 = v1 - v2 + ov.
    
        " das folgende ist signifikant effektiver als ein if/sub
        ov = v3 div chunkmod. " ├ťberlauf ist hier -1
        v3 = v3 mod chunkmod.
    
        append v3 to result.
      enddo.
      " ...Ein ├ťberlauf nach Abschluss kann wegen vorherigem Sortieren nicht auftreten.
    
      " F├╝hrende Nullen l├Âschen
      n = lines( result ).
      do n times.
        i = n - sy-index + 1.
        read table result into v1 index i.
        if ( v1 <> fzero ).
          exit.
        endif.
      enddo.
      " -> alles von i bis n l├Âschen! Aber nur, wenn i > 1 ist!
      delete result from i + 1." to n.
    
      free dat.
      dat = result.
    
      this = me.
    endmethod.
    ENDCLASS.

     

    class documentation:

    CL Z04_BIGINT

    ____________________________________________________

    Kurztext

    Big Integer Arithmetik auf Float-Arrays

    Funktionalit├Ąt

    Stellt BigInteger-Arithmetik auf Basis von internem floating point array bereit.

    Kann bis zu Speicherlimit/Typlimit Zahlenl├Ąnge verarbeiten (Begrenzung nur durch die Maximalgr├Â├če einer internen Tabelle f├╝r Floats bzw. den Integer-Wertebereich f├╝r die Indizierung von Ziffern).

    Philosophie der Schnittstellen-Organisation ist, dass – au├čer in “get…” und “cmp…”-Funktionen – immer das aktuelle Objekt manipuliert wird, gegebenenfalls unter Hinzuziehung eines weiteren Objekts. Das hei├čt, dass der erste Operand aller Operationen immer das aktuelle Objekt ist. Das bedeutet weiterhin, dass alle Operationen mit zwei Operanden (add, mul, …) – der erste davon immer das aktuelle Objekt – inkrementell auf das aktuelle Objekt wirken.

    Zusammen mit dem Umstand, dass alle Operationen, die das aktuelle Objekt ver├Ąndern, eine Referenz auf das aktuelle Objekt liefern, bedeutet dies, dass Operationen nahtlos verkettet werden k├Ânnen:

    a->seti( 1 )->add( b )->mul( c ).

    Eine Ausnahme davon stellt “divx()” dar: Dieses kann den Rest als return-Argument zur├╝ckliefern, manipuliert aber auch inkrementell das aktuelle Objekt. Der Name bekommt deshalb keine Vorsilbe GET, aber nach “->divx()” kann nicht nahtlos weiter verkettet werden. F├╝r letzteres gibt es “div()” und “mod()” getrennt. “div()” und “mod()” benutzen intern “divx()”. “divx” puffert statisch den Rest. Dieser kann bei einem anschlie├čenden “mod()” mit denselben Argumenten unmittelbar danach ohne Neuberechnung abgerufen werden.

    Weiterhin stellen die Vergleichsfunktionen (“cmp()” und “abscmp()”) eine Ausnahme von dieser Regel dar. Die Vorsilbe “get” w├╝rde bei denen einfach albern wirken. Der Vergleich liefert “-1”, wenn das aktuelle Objekt kleiner ist als das Argument “x”, “+1” wenn gr├Â├čer und “0” wenn gleich.

     

    Es gibt eine weitere Klasse “z04_bigintx” (mit Suffix “x”), die mit DecFloat34 arbeitet ud Zahlen mit mehr als 100 Ziffern deutlich schneller verarbeiten kann. Die hier vorliegende Klasse “z04_bigint” ist also eher f├╝r “kleine gro├če” Zahlen geeignet, w├Ąhrend “z04_bigintx” f├╝r “echt gro├če gro├če” Zahlen sinnvoll ist.

     

    Beispiel

    data: a type ref to BigInt,

    b type ref to BigInt,

    s type string.

    create object a, b.

     

    a->setstr( ‘+0123e200’ ).

    s = b->setstr( ‘-0123e200’ )->add( a )->getstr( ).

    write: / s. ” liefert: 0

    Hinweise

    Setter / Getter:

    Es gibt eine ganze Palette von Funktionen zum Einstellen und Abfragen eines Wertes – aus Performance-Gr├╝nden: Ein Teil dieser Funktionen wird intern f├╝r die Implementierung eingesetzt. Sie unterscheiden sich von anderen Funktionen durch einen Pr├Ąfix “set” bzw. “get”. Sie unterscheiden sich untereinander durch ein Suffix, welches den Typ des Arguments kennzeichnet (weil von SAP bisher kein ├ťberladen f├╝r nutzerdefinierte Funktionen bereitgestellt wird). Die schnellsten davon sind die f├╝r Integers (mit “i” als Suffix) bzw. Floats (mit “f” als Suffix). F├╝r alle nicht explizit unterst├╝tzten Zahlenformate ist eine gemeinsame Schnittstelle ├╝ber String (“setstr()” / “getstr()”) vorgesehen.

    “setstr()” versteht Vorzeichen (“+” und “-“) und Zehnerpotenzen in der ├╝blichen Notationsform (‘+123e100’) und toleriert f├╝hrende Nullen sowie Leerzeichen um das ganze drumrum (‘ -00001234e567 ‘ ).

    Alle anderen Setter (mit Zahlen-Argument) k├Ânnen optional einen extra Integer als Zehner-Exponent aufnehmen.

    “setf()” ist nicht wirklich f├╝r die ├ťbergabe sehr gro├čer Zahlen vorgesehen, denn solche float-Werte haben zu Ganzahlen gewandelt eh nur einen Bruchteil ihrer Stellen (die h├Âchsten 15) sinnvoll gesetzt. Der Rest ist Schrott oder Null. Mit “setf()” soll lediglich eine vereinfachte ├ťbernahme von floats bis maximal 10^15 erm├Âglicht werden, wenn diese urspr├╝nglich als float vorliegen. Dies wird unter anderem intern f├╝r das Einstellen von Sch├Ątzwerten beim Dividieren genutz. Falls also viele h├Ąngende Nullen ├╝bergeben werden sollen, ist dazu das optionale Argument “exp” zu nutzen!

    Analoges gilt f├╝r “setdf()”.

    Die Funktionen “getf()” und “getdf()” liefern 8-byte-floats bzw. 16-byte-decfloats als Mantisse und einen extra Integer als Zehner-Exponent zur├╝ck. Letzteres ist notwendig, weil die Exponentenbereiche gew├Âhnlicher floats nur f├╝r winzige Bigints ausreichen (lediglich f├╝r ein paar hundert bis ein paar tausend Stellen). Die Mantissen stellen einen N├Ąherungswert mit mindestens 15 bzw. 29 Dezimalstellen dar.

    Alle Getter liefern den abgefragten Wert zur├╝ck und unterbrechen damit die Verkettbarkeit.

    Alle Setter liefern eine Referenz auf das aktuelle Objekt zur├╝ck und k├Ânnen somit bevorzugt zum Start von Verkettungssequenzen genutzt werden.

    =============================

    Ausnahmen von der syntaktischen Systematik:

    Die Syntax-Ausnahme beim “divx()” ist gewollt. Es soll dort verwendet werden, wo beim Dividieren der Rest sofort gegriffen werden soll. Ist dagegen nahtloses Verketten gew├╝nscht, kann “div()” zusammen mit einem sp├Ąteren “mod()” genutzt werden, ohne dass f├╝r letzteres die Berechnung wiederholt wird. Der Rest wird intern statisch zwischengespeichert. Wenn allerdings zwischen einem “div()” und dem dazugeh├Ârenden “mod()” eine andere Operation “div()” oder “divx()” (mit anderen Operanden) aufgerufen wurde, wird beim Aufruf von “mod()” die Division erneut ausgef├╝hrt. Wenn der Programmierer hier Bl├Âdsinn baut, hat er vom internen Zwischenspeichern nichts. Die Ergebnisse sind dennoch immer korrekt.

     

    Es k├Ânnte zus├Ątzlich auch Funktionen mit zwei Operanden geben (Pr├Ąfix “e”), die jeweils alle ihre Operanden als Argumente ├╝bernehmen. Das Beispiel aus dem einleitenden Abschnitt mit dieser Schnittstellenvariante s├Ąhe so aus:

    a->emul( x = a->eadd( x = a->seti( 1 ) y = b ) y = c ).

    -> Die Syntax w├Ąre auf jeden Fall aufwendiger und bei Verkettungen schlechter lesbar. Momentan sehe ich keinen Anlass, dies zu implementieren.

    =============================

    Warum benutzt Du nicht einfach den Typ “packed” mit 16 Bytes? Oder “DecFloat34”?

    Weil deren Wertebereich nur bis 31 bzw. 34 Dezimalstellen reicht. Schon f├╝r mathematische Aufgaben auf Anf├Ąnger-Niveau (vor Beginn eines Mathematik-Studiums, nur bis Klasse 12/13) sind gr├Â├čere Wertebereiche zwingend notwendig. Schon Mathematiker bei den alten Arabern und im europ├Ąischen Mittelalter haben mit gr├Â├čeren Zahlen experimentiert (die sie damals von Hand berechneten – das waren echte Enthusiasten!) . Wer sich f├╝r solche Experimente nicht begeistern m├Âchte, m├Âge die Finger davon lassen. Wer in die Fu├čstapfen der Entwickler von moderner Verschl├╝sselung treten m├Âchte oder sich sonstwie f├╝r alle m├Âglichen mathematischen Fragestellungen interessiert, muss sich zwingend auch auf solche akademischen Zahlenexperimente mit gro├čen Zahlen einlassen. 4k-RSA etwa braucht f├╝r normale Funktion Zahlen mit etwa 1200 Dezimalstellen (in ├╝blichen “richtigen” Computern werden solche 4-K-RSA-Faktoren als 500 Byte-Werte gespeichert, aber SAP stellt Bytes nur ├╝ber Umwege bereit, die sich nur marginal f├╝r direkte Verarbeitung eignen und auch dies nur in sehr kleinen Bl├Âcken (4 Byte)). Mit DecFloat34 k├Ânnte man etwa gerade so die Berechnung von veralteten 64-bit-Hash-Verfahren nachstellen (bzw. entwickeln). Nat├╝rlich wird man echte Krypto-Bibliotheken niemals in ABAP schreiben – aus besten auf der Hand liegenden Gr├╝nden. Aber ABAP – wenn man es nun schon mal verpflichtend als Sprache im beruflichen Alltag verwenden muss – l├Ąsst sich nichtsdestotrotz f├╝r Experimente – zum Lernen und Forschen – perfekt einsetzen. Wenn man es ein wenig aufbohrt.

    =============================

    Performance:

    ABAP verbr├Ąt pro elementarem Statement der Sprache gr├Â├čenordnungsm├Ąig 100 bis 200 CPU-Takte (analog etwa PHP). Das entspricht auf derzeitigen CPUs einigen zig Nanosekunden pro simpelster Anweisung. Da jede BigInt-Funktion – selbst im einfachsten Fall etwa der Addition – aus mindestens um 10 elementaren Anweisungen einschlie├člich Argumenttests und Sonderfallbehandlung besteht, ist keine BigInt-Funktion unter ein paar hundert Nanosekunden zu bekommen. Dazu kommen Operationswiederholungen proportional der Menge der chunks in den Zahlen.

    Der Aufwand f├╝r Add/Cmp/Set/Get w├Ąchst linear mit der Zahlengr├Â├če.

    Der Aufwand f├╝r Mul w├Ąchst quadratisch.

    Der Aufwand f├╝r Pow w├Ąchst quadratisch zur Basis / linear zum Exponenten.

    Der Aufwand f├╝r Div/Mod/PowMod w├Ąchst kubisch.

     

    Weiterf├╝hrende Informationen

    Anwendungsbereiche: akademische Mathematik, wissenschaftliche Berechnungen, mathematische Experimente, Erlernen der Sprache ABAP, “Euler Project” ab Aufgabe 13:

    https://projecteuler.net/archives

    =============================

    Anregungen zur Bibliothek: Manish Kumar “Integer Arithmetic Operations on Large Numbers”

    https://blogs.sap.com/2013/09/09/integer-arithmetic-operations-on-large-numbers/

    Dieser gegen├╝ber fehlerbereinigte und durch Nutzung von floats statt integers stark beschleunigte Operationen (Addition/Multiplikation: 3-fach, Division: 1000-fach, Potenz: zig bis zigmilliardenfach – je nach Zahlengr├Â├če).

    (0) 

Leave a Reply