C++和往返数字中的舍入

Rounding in C++ and round-tripping numbers

本文关键字:舍入 数字 C++      更新时间:2023-10-16

我有一个类,它在内部将不动点中的一些量表示为32位整数,分母有些任意(既不是2的幂,也不是10的幂)。

对于与其他应用程序的通信,数量在输出时转换为纯旧的双倍,在输入时转换为双倍。作为类内的代码,它看起来像:

int32_t quantity;
double GetValue() { return double(quantity) / DENOMINATOR; }
void SetValue(double x) { quantity = x * DENOMINATOR; }

现在我需要确保,如果我将某个值输出为double并将其读回,我将始终得到相同的值。即

x.SetValue(x.GetValue());

将永远不会改变x.quantity(x是包含上述代码的类的任意实例)。

双精度表示有更多的位数,所以它应该是可能的。但几乎可以肯定的是,上面简单的代码不会是这样。

  • 我需要使用什么舍入以及
  • 我如何找到关键的潜在拐角案例来测试舍入是否确实正确

当你转换为双精度时,任何32位都会被精确地表示,但当你除以然后乘以任意值时,你会得到一个相似的值,但并不完全相同。每次操作最多应该损失一位,这意味着在返回int之前,double将几乎相同。然而,由于int强制转换是截断,当非常小的错误将2.000变为1.999时,您将得到错误的结果,因此您需要在强制转换前执行一个简单的舍入任务。

如果您有C++11,您可以使用std::lround(),否则您可以编写自己的舍入函数。

在这里,您可能不太关心公平性,所以常见的int(doubleVal+0.5)将适用于积极因素。如果看起来很可能,你有消极的一面,试试这个:

int round(double d) { return d<0?d-0.5:d+0.5; }

您描述的问题与使用不同基数在二进制和十进制表示之间转换时存在的问题相同。至少,如果你想让double表示法很好地近似于原始值,它是存在的(否则,你可以将32位的值与固定分母相乘,并将结果存储在double中)。

假设您希望double表示与实际值很好地近似,则转换是不平凡的!从内部表示到double的转换可以使用Dragon4("如何准确打印浮点数",Steele&White)或Grisu。可以使用Bellerophon("如何准确读取浮点数",Clinger)进行反向操作。不过,这些算法并不完全是琐碎的。。。