Перейти до вмісту

Числові операції

Арифметика в ABAP — не просто + і -: важливий calculation type (який тип результату отримаєш), правила округлення при змішаних типах і вибір правильного типу даних для задачі. Помилки тут мовчазні — 1 / 3 присвоєне у i дасть 0, 0.815 у f — це 8.1499999999999995E-01. Ця сторінка збирає те, що треба знати, щоб не наступати.

ТипРозмірДіапазонКоли брати
i4 байти±2 147 483 647лічильники, індекси, sy-index, sy-tabix
int88 байтів±9.2·10^18якщо i замалий
p LENGTH n DECIMALS d1-16 байтівзалежить від довжинифіксовані десяткові — суми, ваги, кількості
decfloat168 байтів16 десяткових розрядіввисокоточні десяткові обчислення
decfloat3416 байтів34 десяткових розрядимаксимальна точність без big int
f8 байтівмантиса + експоненташвидкі обчислення, точність не критична

Тип nне числовий, це символьний із цифрами. Для розрахунків не бери; використовуй для ID та артикулів.

DATA i_a TYPE i VALUE 3.
DATA int8_a TYPE int8 VALUE 1.
" Packed: ± 15 значущих цифр для LENGTH 8, з них 2 на дробову частину
DATA p_val TYPE p LENGTH 8 DECIMALS 2 VALUE '1234567890123.99-'.
DATA dec16 TYPE decfloat16 VALUE '0.7805874561940696'.
DATA dec34 TYPE decfloat34 VALUE '0.0587780463975441530508753423121495'.
" Binary float — обережно, не точний
DATA float TYPE f VALUE '2.1643779466775481E-01'.
float = CONV f( '0.815' ). " 8.1499999999999995E-01
DATA(dec_b) = CONV decfloat34( '0.815' ). " 0.815
ASSERT float <> dec_b.

Узагальнені типи (numeric, p, decfloat) дозволяють писати код, що працює з будь-якою спеціалізацією. Використовуються для field symbols і параметрів методів.

FIELD-SYMBOLS <num> TYPE numeric. " приймає все числове
FIELD-SYMBOLS <p> TYPE p. " тільки packed
FIELD-SYMBOLS <d> TYPE decfloat. " decfloat16 і decfloat34
ASSIGN i_a TO <num>.
ASSIGN dec16 TO <d>.
ASSIGN p_val TO <p>.

Оскільки більшість типів (крім i) не можна задати напряму — їх записують як character-літерали:

DATA(math) = `- 123.45`. " математична: знак зліва, пробіл опційний
DATA(comm) = `123.45-`. " комерційна: знак справа
DATA(sci) = `-1.23456E03`. " наукова

Що який тип приймає:

Типmathcommsci
decfloat16/34тактактак
fтак (без пробілу після знака)тактак
i, int8, pтактакні (CX_SY_CONVERSION_NO_NUMBER)

+, -, *, /, DIV, MOD, **.

DATA res TYPE i.
res = 7 DIV 3. " 2 — цілочисельна частина
res = 7 MOD 3. " 1 — залишок (завжди додатний)
res = 2 ** 5. " 32 — піднесення до степеня

Пріоритет стандартний: ** > * / DIV MOD > + -. Дужки пріоритезують обчислення всередині.

Винятки при обчисленнях

Section titled “Винятки при обчисленнях”
" Ділення на нуль
TRY.
res = 1 / 0.
CATCH cx_sy_zerodivide.
ENDTRY.
" 0/0 — НЕ викидає виняток, результат просто 0
res = 0 / 0. " 0
" Переповнення
TRY.
res = 2147483647 + 1. " max_i + 1
CATCH cx_sy_arithmetic_overflow.
ENDTRY.

** зазвичай дає результат типу f. Якщо не хочеш binary float — бери функцію ipow:

DATA(a) = 2 ** 5. " тип f (!)
DATA(b) = ipow( base = 2 exp = 3 ). " тип i, результат 8
a += b. " a = a + b
a -= b. " a = a - b
a *= b. " a = a * b
a /= b. " a = a / b

Calculation type — тип проміжних обчислень у виразі. Визначається за найбільшим з учасників за таким порядком пріоритету:

  1. decfloat34 — якщо хоч один учасник decfloat34
  2. decfloat16 — якщо хоч один decfloat16
  3. f — якщо хоч один f (крім ситуацій з decfloat)
  4. p — якщо хоч один p
  5. int8 — якщо хоч один int8
  6. i — інакше

Конверсії нечислових типів: d, t, x, xstringi; c, n, stringp; utclong — не підтримується.

DATA(r1) = 1 + CONV decfloat34( '1.1' ). " decfloat34
DATA(r2) = CONV decfloat16( '2.2' ) + 5. " decfloat16
DATA(r3) = 1 + CONV f( '2.23645E-01' ). " f
DATA(r4) = CONV int8( 2 ) + 2. " int8
DATA(r5) = 2 + 5. " i

EXACT перевіряє, що при присвоєнні/обчисленні не відбулось небажаного округлення — інакше викидає CX_SY_CONVERSION_ROUNDING:

TYPES ty_pl8d2 TYPE p LENGTH 8 DECIMALS 2.
DATA(ok) = EXACT ty_pl8d2( 1 / 4 ). " 0.25 — ок
TRY.
DATA(bad) = EXACT ty_pl8d2( 1 / 3 ). " потрібне округлення → виняток
CATCH cx_sy_conversion_rounding.
ENDTRY.

Вбудовані числові функції

Section titled “Вбудовані числові функції”
" Абсолютна величина і знак
abs( -4 ). " 4
sign( -789 ). " -1 (0 якщо 0, 1 якщо додатне)
" Округлення
ceil( CONV decfloat34( '4.001' ) ). " 5 — вгору до цілого
floor( CONV decfloat34( '4.999' ) ). " 4 — вниз до цілого
trunc( CONV decfloat34( '-4.999' ) ). " -4 — ціла частина (відкидання)
frac( CONV decfloat34( '4.999' ) ). " 0.999 — дробова частина
" round: до N розрядів або до precision
round( val = CONV decfloat34( '1.2374' ) dec = 2 ). " 1.24
round( val = CONV decfloat34( '1234' ) prec = 3 ). " 1.23E+3
" rescale — аналогічно, з більш жорсткими правилами scale/precision
rescale( val = CONV decfloat34( '1234.56789' ) dec = 1 ). " 1234.6
" Степінь і квадратний корінь
ipow( base = 2 exp = 3 ). " 8 (цілочисельна, без f)
sqrt( CONV decfloat34( '40.96' ) ). " 6.4
" Логарифми, тригонометрія
log10( CONV decfloat34( '1000' ) ). " 3
sin( '30' ). cos( '45' ). tan( '90' ).
" Також: acos, asin, atan, cosh, sinh, tanh, exp, log
" Екстремуми — від 2 до 9 аргументів
nmin( val1 = ... val2 = ... val3 = ... ).
nmax( val1 = ... val2 = ... val3 = ... ).
" Факторіал і біноміальний коефіцієнт
factorial( 10 ). " 3628800
binomial( n = 5 k = 2 ). " 10

Системні класи для обчислень

Section titled “Системні класи для обчислень”
DATA(max_i) = cl_abap_math=>max_int4. " 2147483647
DATA(max_i8) = cl_abap_math=>max_int8.
DATA(max_d34) = cl_abap_math=>max_decfloat34.
" min_int1/2/4/8, max_int1/2, min_decfloat16/34 — усе аналогічно

cl_abap_random* — випадкові числа

Section titled “cl_abap_random* — випадкові числа”
DATA(rand_i) = cl_abap_random_int=>create( seed = cl_abap_random=>seed( )
min = 1 max = 100 )->get_next( ).
DATA(rand_d34) = cl_abap_random_decfloat34=>create( seed = cl_abap_random=>seed( ) )->get_next( ).
DATA(rand_f) = cl_abap_random_float=>create( seed = cl_abap_random=>seed( ) )->get_next( ).
" Є варіанти для int8, packed, packed_dec1..dec14

cl_abap_bigint — цілі довільної розрядності

Section titled “cl_abap_bigint — цілі довільної розрядності”

Коли числа виходять за межі int8cl_abap_bigint. API fluent, всі операції повертають новий екземпляр (якщо не викликати clone, то метод модифікує той самий об’єкт).

DATA(big) = cl_abap_bigint=>factory_from_string( `283469208407283452340` ).
DATA(plus) = cl_abap_bigint=>factory_from_int4( 4 )->add( big )->to_string( ).
DATA(pow) = cl_abap_bigint=>factory_from_int4( 8 )->pow( 2 )->to_string( ). " "64"
DATA(sqrt1) = cl_abap_bigint=>factory_from_int4( 9 )->sqrt( )->to_string( ). " "3"
" Ділення: повертає структуру з quotient і remainder
DATA(dv) = cl_abap_bigint=>factory_from_int4( 20 )->div( big ).
DATA(q) = dv-quotient->to_string( ).
DATA(r) = dv-remainder->to_string( ).
" Конвертації
cl_abap_bigint=>factory_from_int4( 100 )->to_utf8( ).
cl_abap_bigint=>factory_from_string( `123` )->to_df34( ).
cl_abap_bigint=>factory_from_int4( -10 )->to_external( iv_flg_minus_in_front = abap_true ).

cl_abap_rational — раціональні числа

Section titled “cl_abap_rational — раціональні числа”

Точні дроби без округлення:

DATA(r) = cl_abap_rational=>factory_from_string( `-2/3` )->add_int4( 1 )->to_df34( ).
" 3.333...E-01 — як decfloat34
" Або в packed
DATA result TYPE p LENGTH 16 DECIMALS 5.
cl_abap_rational=>factory_from_string( `-2/3` )->add_int4( 1
)->to_dec( IMPORTING ev_decimal = result ).

Обчислення з датою, часом і time stamp

Section titled “Обчислення з датою, часом і time stamp”

Детально — у Дата, час і час-мітка. Коротко:

" Дати: різниця у днях
DATA d1 TYPE d VALUE '20240101'.
DATA d2 TYPE d VALUE '20231227'.
DATA(days) = d1 - d2. " 5
" Час: різниця у секундах (може бути від'ємною)
DATA t1 TYPE t VALUE '210000'.
DATA t2 TYPE t VALUE '040000'.
DATA(diff_s) = ( t2 - t1 ) MOD 86400. " 25200 — абсолютна за добу
DATA(hours) = diff_s DIV 3600.
DATA(minutes) = ( diff_s MOD 3600 ) DIV 60.
DATA(seconds) = diff_s MOD 60.
" Time stamp: utclong_add / utclong_diff
DATA(ts) = utclong_current( ).
DATA(ts_plus) = utclong_add( val = ts hours = 1 ).
DATA(sec_diff) = utclong_diff( high = ts_plus low = ts ). " 3600
SELECT SINGLE
1 + 2 AS add, " 3
10 - -8 AS subtract, " 18
3 * 5 AS multiply, " 15
" / заборонено для цілих — треба типізовані літерали або CAST
d34n`4.4` / d34n`2.2` AS divide1, " 2
CAST( 1 AS D34N ) / CAST( 2 AS D34N ) AS divide2, " 0.5
( 1 + 2 ) * 3 AS parens, " 9
" Функції
div( 4, 2 ) AS idiv, " 2 (округлене до int)
division( 1, 3, 2 ) AS ddiv, " 0.33 (з округленням)
mod( 3, 2 ) AS md, " 1
ceil( decfloat34`1.333` ) AS c, " 2
floor( decfloat34`1.333` ) AS fl, " 1
abs( int4`-2` ) AS a, " 2
round( decfloat34`1.337`, 2 ) AS r " 1.34
FROM i_timezone
INTO @DATA(wa).

Адаптовано з 29_Numeric_Operations.md (Apache 2.0). Повний перелік нюансів — в оригіналі.