【发布时间】:2014-11-26 21:03:13
【问题描述】:
在 C++ 中是否有一种算法允许我在给定类型 T 的浮点值 V(例如 double 或 float)的情况下,在给定方向(向上或向下)返回最接近 V 的值以小于或等于指定的小数位数 D 表示准确?
例如,给定
T = double
V = 670000.08267799998
D = 6
对于方向 = 朝向 +inf,我希望结果为 670000.082678,对于方向 = 朝向 -inf,我希望结果为 670000.082677
这有点类似于 std::nexttoward(),但有一个限制,即“下一个”值需要使用最多 D 个小数位精确表示。
我考虑过一种简单的解决方案,包括分离小数部分并将其缩放 10^D、截断它,然后再次缩放 10^-D 并将其添加回整数部分,但我没有t 相信这保证了结果值将在基础类型中完全可表示。
我希望有一种方法可以正确地做到这一点,但到目前为止我一直找不到。
编辑:我认为我最初的解释没有正确传达我的要求。在@patricia-shanahan 的建议下,我将尝试描述我的更高层次的目标,然后在这种情况下重新表述问题。
在最高级别,我需要这个例程的原因是由于一些业务逻辑,其中我必须接受一个双精度值 K 和一个百分比 P,将其拆分为两个双精度组件 V1 和 V2,其中 V1 ~= P 百分比K 和 V1 + V2 ~= K。要注意的是,V1 在通过有线协议发送到第三方之前用于进一步的计算,该有线协议接受字符串格式的浮点值,最多 D 位小数。因为发送给第 3 方的值(字符串格式)需要与使用 V1(双格式)进行的计算结果相一致,所以我需要使用某些函数 F()“调整”V1,使其如下所示尽可能接近 K 的 P%,同时仍然可以使用最多 D 个小数位精确表示为字符串格式。 V2 没有 V1 的限制,可以计算为 V2 = K - F(V1)(可以理解和可以接受的是,这可能导致 V2 使得 V1 + V2 非常接近但不完全等于 K) .
在较低级别,我希望编写该例程来“调整”V1,并具有以下签名:
double F(double V, unsigned int D, bool roundUpIfTrueElseDown);
其中的输出是通过取 V 和(如有必要,按 bool 参数指定的方向)将其四舍五入到小数点后第 D 位来计算的。
我的期望是当 V 被如下序列化出来时
const auto maxD = std::numeric_limits<double>::digits10;
assert(D <= maxD); // D will be less than maxD... e.g. typically 1-6, definitely <= 13
std::cout << std::fixed
<< std::setprecision(maxD)
<< F(V, D, true);
那么输出仅包含第 D 个小数位以外的零。
请务必注意,出于性能原因,我正在寻找不涉及在双精度和字符串格式之间来回转换的 F() 实现。虽然输出最终可能会转换为字符串格式,但在许多情况下,逻辑会在必要之前提前输出,我希望避免这种情况下的开销。
【问题讨论】:
-
在您的示例中,两个结果相差 0.000001,这不能完全表示为二进制浮点数,因此至少其中一个也不能完全表示。
-
我的意思是,其中至少有一个是不可表示的。
-
更进一步,考虑到这样一个特性的行为显然不是你所期望的,它是否有一个有用的目的是值得商榷的。
-
@ChrisKline 你能把问题描述上一层吗?你会用它做什么?最好的解决方案可能是使用十进制类型。可能是接受一个双精度数,当四舍五入到 D 位时,它会显示为您想要的值。
-
D似乎是小数点之后的位数,而不是总数。您确定要 浮点 点吗?定点算术呢?
标签: c++ c floating-point decimal precision