【发布时间】:2013-05-15 07:31:37
【问题描述】:
我正在开发一种软件,该软件可以在文本和内部 (double) 表示之间转换测量的数字。该过程的一个必要部分是根据测量的统计不确定性生成具有正确小数精度的文本表示。所需的精度随数字而变化,其中的最低有效位可以是任何位置,包括(十进制)单位位置的左侧。
正确的舍入对于此过程至关重要,其中“正确”意味着根据当时有效的浮点舍入模式,或至少在明确定义的舍入模式下。因此,我需要小心(阅读:避免)对正在处理的数字执行中间算术,因为舍入甚至可能对数字内部表示中的最低有效位敏感。
如果我首先计算所需表示中的有效位数,我认为我可以使用 printf 系列函数相当好地完成几乎所有需要的格式化:
sprintf(buffer, "%.*e", num_sig_figs - 1, number);
到目前为止,有一类极端情况让我失望,但是:测量数字中的最高有效(十进制)数字位于所需精度表示的最低有效数字的右边一位。在这种情况下,四舍五入应在所需结果中产生最小(也是唯一)有效数字为 0 或 1,但我无法设计一种方法以可移植(*)方式执行舍入而不会有风险改变结果。这类似于 MPFR 函数mpfr_prec_round() 可以做的,除了它以二进制精度工作,而我需要使用十进制精度。
例如,在默认舍入模式下(从最接近的舍入到偶数舍入):
- 0.5 表示为单位 (10^0) 精度应为“0”或“0e+00”
- 654 表示为千 (10^3) 精度应为“1e+03”
- 0.03125 表示为十分之一 (10^-1) 精度应为“0”或“0e-01”甚至“0e+00”
(*) 这里的“可移植”是指代码准确地表达了标准的、可移植的 C99(或更好的 C90)中的计算。据了解,实际结果可能取决于机器细节,它应该取决于(并与)生效的浮点舍入模式。
我有什么选择?
【问题讨论】:
-
十进制数和双精度数的舍入不能很好地结合在一起......(小数部分在二进制中是周期性的)尝试使用一些定点数据类型......
-
看起来这些会用
if (x < precision) if (x <= .5*precision) printf("0") else printf("%.0e", precision) else /* Not one of these cases, do other stuff. */…之类的代码来处理。是否存在您想要的与此代码产生的不同的情况? (precision的舍入方式存在问题,因为 .1 无法准确表示,因此您可能在边界处遇到一些问题。此外,不处理负数x,但这很简单。)跨度> -
请更好地解释您认为您的
sprintf方法有问题的情况。我认为这是正确的方法,我对您遇到的问题感到困惑。 -
sprintf 方法不适用于问题案例,因为 sprintf 将其结果基于参数的最高有效数字,(在这种情况下)不是所需结果的有效数字。例如,
sprintf(buffer, "%.0e" 0.5)产生“5e-01”而不是“0e+00”。只有在参数四舍五入的情况下,您才会得到非零结果。 -
我意识到十进制舍入与二进制浮点不能很好地协调,并且大多数十进制分数都有无限的二进制表示。事实上,我的内部表示是一种有点笨拙的任意精度十进制格式。但是,我需要能够以标准浮点格式将数字接受到系统中,并且必须正确转换它们。