【发布时间】:2012-12-07 11:37:06
【问题描述】:
简介
我继承了一个使用 VS2008、xlw 2.0 和 excel 2003 开发的遗留插件项目(C++)。
我已设法将它移植到 xlw 4.0 标头(添加对 Excel 2007 的支持时,某些方法更改了名称,诸如此类)。我没有生成任何东西(还),因为旧代码是手工编写的,我不会更改正在运行的东西,并且只是为了它而具有合理的结构和代码逻辑。
代码在 excel 2003 下按预期工作。我今天尝试使用 excel 2010 并发现了一个最令人费解(和可怕)的错误。
令人费解的行为
我(从 VBA 宏)接收一列(n 行,1 列)数字(通过构造的小正整数)作为 XlfOper & 我想将其转换为(旧式)数组。代码基本上遍历每一行,然后在与给定行对应的 XlfOper 上调用 AsInt() 方法。比如:
for (long iy = 0; iy < numcols; ++iy)
{
for (long ix = 0; ix < numrows; ++ix)
convertXflOper( x(ix, iy), a1D[iy*numrows + ix] );
}
其中 a1D 是此特定项目中使用的一维数组类型,convertXflOper 是模板函数,适用于这些类型:
void convertXflOper (const XlfOper &x, long & y)
{
y = x.AsInt();
}
在给定的行中,我在 excel 工作簿中有数字 10。
在 Excel 2003 下,此值转换为 10。在 Excel 2010 下,此值转换为 9。
我已经对其进行了调试,问题是它实际上读取的不是 10,而是它的双重近似值。 (即时窗口的)输出是
x.lpxloper4_->val.num
9.9999999999999982 // wrong: I am not asking for 10.0, I am asking for 10
在 Excel 2010 中,以及
x.lpxloper4_->val.num
10.000000000000000 // right: an integer is an integer
在 Excel 2003 中。因此,尽管 C++ 代码很漂亮(或不漂亮),但似乎没有错。 AsInt() 方法的行为类似于强制转换,static_cast<long>(9.9999999999999982) 是 9,而不是 10。
问题
这件事让我很伤心,主要是因为它很容易被忽视:对于许多其他整数值,它在 excel 2003 和 2010 中都能正常工作。
我在 excel 界面中做错了什么,因为它认为整数必须被视为双精度数?我怎么能确定这不会在谁知道何时何地发生?解决此问题的最佳方法是什么?
【问题讨论】:
-
我认为 excel 在内部处理浮点数是有意义的。也许在新版本的 excel 中舍入的内部行为发生了变化。如果您可以访问
x.AsDouble();之类的方法,您应该自己简单地进行舍入。 -
@Fermat2357 很好,但是整数应该被精确表示,这似乎是一个基本的期望。我可以使用这种方法,问题是确保我在可能需要它的所有情况下都应用它......这种动态输入不容易预测何时需要出现,并确保手动转换适用于任何地方。
-
是的,你是对的。足够小的整数可以精确地表示为双精度数。如果我们假设预期的整数是浮点格式的非常接近的近似值,那么将舍入方法应用于转换为整数应该可以正常工作。如果我们不能假设这一点,你就有麻烦了。你至少应该测试一下。
-
那个单元格的公式是什么?似乎在某个地方它涉及一些不返回整数的数学。
-
@MarkRansom 不错的猜测:但它是一个静态值。它基本上是计算行中大于零的单元格的数量,但不是公式:它是由(以前的,独立的)宏写为值。
标签: c++ visual-studio excel visual-studio-2008 xls