CString.Format 以相同的精度生成不同的值

Posted

技术标签:

【中文标题】CString.Format 以相同的精度生成不同的值【英文标题】:CString.Format produces different values with the same precision 【发布时间】:2020-12-23 21:17:32 【问题描述】:

抱歉,这个问题可能很愚蠢,因为我是 C# 开发人员,我真的很难找到一些想法。

我们正在从 VC++ 2013 迁移到 VC++ 2019。我们有 2 个安装了 VC++2013 和 VC++2019 的类似 VM。项目已经在使用 VC++2019。在 2 个 VM 上运行时,简单评估的结果不同:

CString Value;
int precision = 8;
double number = 50.634765625;
String^ RetValue;

Value.Format(_T("%.*f"), precision, number);
RetValue = gcnew String(Value.GetString());

在一个虚拟机(Win 7 64 位)上,我们在另一个虚拟机(win10 64 位)上有 RetValue “50.63476563” - “50.63476562” 我期望的所有工具都是相同的(很难说 100%,因为安装了很多,但是最重要的 c++ 是)。

在 VS2013 下转换和运行项目时,两个 VM 上的结果相同 - “50.63476563”。

是否有任何控制格式的选项设置?或者为什么选择一个 VM 截断来支持舍入?

更新 所以这个问题真的与VS2013/VS2019的区别有关。我找到了为什么我们在使用相同 VS2019 的 2 个虚拟机上得到不同结果的原因。所以:

    VS2013 舍入 50.634765625 == VS 2019 舍入用于发布 VS2013 舍入 50.634765625 != VS 2019 舍入进行调试

在这两种情况下,舍入方向都是最接近的。因此,MSFT 在发布/调试中以不同方式解释到最接近的位置。使 /MD 与 /MDd 不同的 C++ 编译器选项。

除了切换到 /MD 以强制舍入为调试中的整个项目采用旧方法外,找不到任何其他方法。

【问题讨论】:

String^ -- 更改或更新您的标签。这不是 C++。 看起来 2013 年使用“四舍五入”,而 2019 年使用“四舍五入”。我不知道你会如何控制它。 我很想这是 Is floating point math broken? 的另一种情况,但事实并非如此 - 50.634765625 是少数可以准确无误地表示的浮点数之一。 您可以使用_control87和朋友设置舍入模式。您是针对 32 位还是 64 位架构进行编译?这很重要,因为 32 位目标编译为 FPU 代码,而 64 位目标始终使用 SSE 单元进行浮点计算。 @pau OP 将他们的 MFC CString 转换为 C++/CLI String^ 的事实与问题无关。 检查std::fegetround 在每个系统上返回什么en.cppreference.com/w/cpp/numeric/fenv/feround 【参考方案1】:

我们在测试中发现 2013 年和 2017 年之间存在差异。我们看到将数字 .249500 呈现为三位数的差异。 2013 = 0.250,而 2017 = 0.249。考虑这个例子:

#include <stdio.h>

// little endian bytes, to avoid parsing differences between both compilers
unsigned char bytesOfDouble[] = 0x56, 0x0E, 0x2D, 0xB2, 0x9D, 0xEF, 0xCF, 0x3F; 
double& dVal = reinterpret_cast<double&>(bytesOfDouble);

void PrintDouble(const double& d)

    printf("printf (%%.3f): %.3f\n", d);
    printf("printf (%%.3g): %.3g\n", d);

    printf("printf (%%f)  : %f\n", d);
    printf("printf (%%g)  : %g\n", d);


int main(int argc, char** argv)

    PrintDouble(dVal);
    return 0;

如果你用 VS2013 和 VS2017 编译,你会得到不同的结果。如果你尝试 g++ 或 clang,你会得到与 VS2017 相同的结果。

如何在两者之间得到相同的结果? IDK...我认为 MS 肯定改变了他们的底层 C 运行时。

【讨论】:

VS 2013 之前与 2017 之后切换到 UCRT,因此 Floating-point migration issues 可能是相关的 (cc @Yauhen.F)。 所以问题确实与VS2013/VS2019的区别有关。我找到了为什么我们在使用相同 VS2019 的 2 个虚拟机上得到不同结果的原因。看起来 MSFT 更改了舍入默认值,但仅适用于调试。所以:1)VS2013 舍入 50.634765625 == VS 2019 舍入版本 2)VS2013 舍入 50.634765625 != VS 2019 舍入 Debug 试图找到统一的方法 @dxiv 我想我记得我曾尝试进入运行时,但情况完全不同......我认为您在 2013 年之后的某个时间(对于 2015 年或 2017 年)切换到 UCRT 是正确的。跨度> .2495 不是一个好的测试值,因为与问题中使用的值不同,它在基数 2 中并不精确。它实际上是 0.2494999999999999995559107... 嗯,在我们的一项长期统计测试中,这是计算的结果。上面程序中的那些确切字节。它们在 VS2017 图表中的图例上呈现不同,0.249 与 0.250。因此,回归测试向我们表明了变化。查看计算后,我们可以看到双精度的字节在我们的两个版本中完全相同,然后得出结论,运行时给出了不同的结果。当你进行计算时,你不能总是保证结果会精确地以 2 为基数呈现。实际上,它不会更常见不是吗?【参考方案2】:

因此,就我而言,它看起来像是 Microsoft 在 Windows 10 SDK (10.0.19041.1) 中引入的错误。 现在将使用一些解决方法。

https://developercommunity.visualstudio.com/content/problem/1085732/different-printf-double-rounding-behaviour-between.html

【讨论】:

以上是关于CString.Format 以相同的精度生成不同的值的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 boost 生成不同位的随机多精度整数?

根据已有样本标签数据制作不同比例的分类样本数据进行遥感图像分类并作精度评价

给定 %d 时,VC++ 6.0 应用程序在 CString::Format 内崩溃

CString.Format

加载相同的保存模型后,Keras 模型精度有所不同

CString .Format