4

在 AS/400 中使用 COMPUTE 功能时,我遇到了一个神秘的问题。

场景如下:

01  WSAA-AMOUNT-A               PIC S9(15)V9(02) COMP-3.
01  WSAA-AMOUNT-B-01            PIC S9(16)V9(02) VALUE 0.
01  WSAA-AMOUNT-B-02            PIC S9(13)V9(05) VALUE 0.
01  WSAA-AMOUNT-C               PIC S9(16)V9(02) VALUE 0.
01  WSAA-RESULT                 PIC S9(15)V9(02) VALUE 0.

MOVE 2500.87             TO WSAA-AMOUNT-A. 
MOVE 12285               TO WSAA-AMOUNT-B-01.
MOVE 12285               TO WSAA-AMOUNT-B-02.
MOVE 4387.5              TO WSAA-AMOUNT-C. 

COMPUTE WSAA-RESULT ROUNDED = (WSAA-AMOUNT-A / ( WSAA-AMOUNT-B-01 + WSAA-AMOUNT-C) * 100 ).
DISPLAY WSAA-RESULT.

COMPUTE WSAA-RESULT ROUNDED = (WSAA-AMOUNT-A / ( WSAA-AMOUNT-B-02 + WSAA-AMOUNT-C) * 100 ).
DISPLAY WSAA-RESULT.

结果令我惊讶的是,第一个公式的结果 = 14.90,而第二个公式的结果 = 15

听起来后者更合乎逻辑,因为 2500.87 / (12285+4387.5) * 100 = 14.99997001。我希望第一个结果的结果在四舍五入后也应该是 15。

有谁知道这些不一致结果的根本原因是什么?

4

1 回答 1

4

取得的结果是 14.90 和 15.00。

COMPUTE 格式不正确。乘以 100 时,请尽早执行,因为如果最后执行,两位小数会消失(除以 100 时,请最后执行,这样您就不会在早期丢失有效数字)。

它不仅适用于 100,因此请考虑一下。首先乘法,最后除法,无论涉及什么值。完全没有必要使用小数点后五位的字段来获得“正确”的答案,除非您在最终答案中需要五位小数。

计算机不是计算器或电子表格。它根据您的要求使用中间结果,而不是其他结果,这些结果为您提供许多小数位。您使用您使用的字段中的小数位数要求小数精度。

了解正在发生的事情的一个关键是精细手册。另一个是实验。这通常是一个普遍的答案。

COMPUTE WSAA-RESULT ROUNDED = 
                  (WSAA-AMOUNT-A / ( WSAA-AMOUNT-B-01 + WSAA-AMOUNT-C) * 100 )

显示 WSAA 结果。得到答案 14.90(十四点九零)。

COMPUTE WSAA-RESULT = 
                  (WSAA-AMOUNT-A / ( WSAA-AMOUNT-B-01 + WSAA-AMOUNT-C) * 100 )

显示 WSAA 结果。通过删除 ROUNDED 得到答案 14.00(十四点零零)。

COMPUTE WSAA-RESULT ROUNDED = 
                  (WSAA-AMOUNT-A / ( WSAA-AMOUNT-B-02 + WSAA-AMOUNT-C) * 100 )

DISPLAY WSAA-RESULT 通过使用五个小数位得到 15.00(15 点零零)“正确”“预期”答案。

记住,你最后乘以 100。所以在此之前的值是 0.149(小数点后三位)、0.14(小数点后两位)和 14.99999n(小数点后六位,我不会再做计算了,所以我将第六个保留为“n”)。

为什么有三位、两位和六位小数?六是因为B-02(有五位,ROUNDED加一位,需要的小数位增加一位,这样就可以计算ROUNDED了),二是因为B-01(有两位小数) 三是因为B-01和ROUNDED。

这是一个重新设计的 COMPUTE,分为三个版本:

COMPUTE WSAA-RESULT ROUNDED =  ( ( WSAA-AMOUNT-A * 100 )
                               / ( WSAA-AMOUNT-B-01 
                                 + WSAA-AMOUNT-C ) ) 

COMPUTE WSAA-RESULT         =  ( ( WSAA-AMOUNT-A * 100 )
                               / ( WSAA-AMOUNT-B-01 
                                 + WSAA-AMOUNT-C) ) 

COMPUTE WSAA-RESULT ROUNDED = ( ( WSAA-AMOUNT-A * 100 ) 
                              / ( WSAA-AMOUNT-B-02 
                                + WSAA-AMOUNT-C) ) 

结果是 15.00、14.99 和 15.00。

这与 Enterprise Cobol 一起使用。似乎 OpenCobol 使用了不同的中间结果,这些结果可能已记录在案。

这是关于中间结果的企业 Cobol 编程指南:

“要了解有关中间结果的信息,您需要了解以下术语。... d 中间结果的小数位数。(如果您使用 ROUNDED 短语,则可能会多带一位小数以确保准确性,如果dmax 在特定语句中,以下各项中的最大值: v 最终结果字段所需的小数位数 v 为除除数或指数以外的任何操作数定义的最大小数位数 v 外部-任何函数操作数的 dmax"

"Operation ... Decimal places
+ or - ... d1 or d2, whichever is greater
* ... d1 + d2
/ ... (d2 - d1) or dmax, whichever is greater"

在“错误”示例中,加法得到两位小数(加法时 d1 和 d2 均为 2),除法得到 3 位小数(dmax 为 3),乘法得到 3(2 + 0 + 1 表示 ROUNDED )。尝试舍入后的最终答案(它永远不会运行,因为两个低位小数由于乘以 100 始终为零),被截断到小数点后两位。

对于 ROUNDED B-01,dmax 为 3(结果字段为 2 位,加 1 用于舍入)。对于普通的 B-01,dmax 为 2。ROUNDED B-02 dmax 为六。

请注意,不仅显示的示例是错误的。ROUNDED 在问题的第一个示例中从不运算,并且始终会删除一个有效小数,因此您只能在舍入前的答案为十分之一时获得正确答案。然后,这些正确答案会被另外九个答案所掩盖,这些答案由于截断而变得相同。

如果你总是写出有效的 COMPUTE,你就不会遇到问题。ROUNDED 仅对最终结果进行操作。如果您需要任何中间字段 ROUNDED,请单独计算并在新计算中使用结果。

计算有效。COMPUTE 不像计算器/电子表格那样操作。任何其他 Cobol 动词也没有(它们是动词而不是函数)。考虑如何编写 COMPUTE 以免失去意义。阅读手册。实验。重复直到正确和理解。

于 2013-01-13T00:37:29.870 回答