1. 问题的提出
Ceil( )是ABAP的向上取整函数,然在在使用中,还是要留意数据类型的问题哦。先看下面代码:
DATA lv_result_1 TYPE i.
DATA lv_result_2 TYPE i.
DATA lv_count_1 TYPE i VALUE '3600'.
DATA lv_count_2 TYPE i VALUE '3001'.
DATA lv_size TYPE i VALUE '1000'.
lv_result_1 = ceil( lv_count_1 / lv_size ).
lv_result_2 = ceil( lv_count_2 / lv_size ).
WRITE:/ 'Result 1 is: ' && lv_result_1.
WRITE:/ 'Result 2 is: ' && lv_result_2.
猜一猜结果?
3600 / 1000的结果是3.6 , 向上取整应该是 4;
3001 / 1000的结果是3.001,向上取整也应该是 4。
但实际的结果呢?第一个是4, 第二个却是3。问题出在哪了?
2. 数字类型的ABAP变量
在ABAP中有以下几种常见的数字类型的变量:
类型 | 长度 | 描述 |
---|---|---|
i | 4 byte | 整形,只能存储整数,范围 -2,147,483,648 to +2,147,483,647 (推荐使用) |
int8 | 8 byte | 长整形,只能存储整数,范围 -9,223,372,036,854,775,808 to +9,223,372,036,854,775,807(推荐使用) |
f | 8 byte | 二进制浮点数,可存储小数(f类型会有些问题,现在已不建议再使用此类型) |
p | 1 ~ 16 byte | 压缩数类型,可存储小数,自定义长度和小数点位数 (尽量用decfloat16和decfloat34替代) |
decfloat16 | 8 byte | 十进制浮点数,可最长有16位小数位(推荐使用) |
decfloat34 | 16 byte | 十进制浮点数,可最长有34位小数位(推荐使用) |
看完此处的总结,便可以分析上例Ceil( )的运算结果的原因了。
lv_result_1 = ceil( lv_count_1 / lv_size ).
lv_result_2 = ceil( lv_count_2 / lv_size ).
这里面的运算数都是整形,因此在ceil( )运算后,发生了隐式的“四舍五入”。需要注意的是,此处即使是动态声明结果变量,其结果还是一样的。
data(lv_result_1) = ceil( lv_count_1 / lv_size ).
data(lv_result_2) = ceil( lv_count_2 / lv_size ).
因为ceil( )运算返回个结果数据类型为整形,所以,动态声明出来的变量也会是整形,在接收结果时还是会发生“四舍五入”。
3. 解决办法
了解完原理,其解决办法也很简单,也即在Ceil( )时,要保证中间结果是“小数类型”。看下例:
DATA lv_upper_result TYPE i.
DATA lv_float_result TYPE p LENGTH 8 DECIMALS 3.
DATA lv_count TYPE i VALUE '3001'.
DATA lv_size TYPE i VALUE '1000'.
lv_float_result = lv_count / lv_size.
lv_upper_result = ceil( lv_float_result ).
WRITE:/ 'Float result is: ' && lv_float_result.
WRITE:/ 'Upper result is: ' && lv_upper_result.
这样就保证接收到的“除法运算”结果为小数,在小数的基础上,再向上取整,便不会发生“四舍五入”的意外。
运行结果为:
p类型的使用需要指定长度和小数位,并不是最优的解决方法,在较高版本的Netwear上,更推荐使用decfloat16和decfloat34这两种类型,它们可以动态地拓展所需要的小数位,并不需要手动指定。
代码如下:
DATA lv_upper_result TYPE i.
DATA lv_float_result TYPE decfloat16. " decfloat34
DATA lv_count TYPE i VALUE '3001'.
DATA lv_size TYPE i VALUE '1000'.
lv_float_result = lv_count / lv_size.
lv_upper_result = ceil( lv_float_result ).
WRITE:/ 'Float result is: ' && lv_float_result.
WRITE:/ 'Upper result is: ' && lv_upper_result.
当然,我们可以用动态声明变量的方式,将这段代码写的更简洁:
DATA lv_count TYPE i VALUE '3001'.
DATA lv_size TYPE i VALUE '1000'.
DATA(lv_float_result) = CONV decfloat16( lv_count / lv_size ).
DATA(lv_upper_result) = ceil( lv_float_result ).
WRITE:/ 'Float result is: ' && lv_float_result.
WRITE:/ 'Upper result is: ' && lv_upper_result.
其实,也可以再夸张一点:
DATA lv_count TYPE i VALUE '3001'.
DATA lv_size TYPE i VALUE '1000'.
DATA(lv_upper_result) = ceil( CONV decfloat16( lv_count / lv_size ) ).
WRITE:/ 'Upper result is: ' && lv_upper_result.
运行结果:
怎么样?通过以上的解析,你清楚了么 😉
本博客专注于技术分享,干货满满,持续更新。
欢迎关注❤️、点赞👍、转发📣!