难以编码的双重最低有效字节是一个好的舍入策略吗?

jpo*_*o38 0 c++ floating-point precision rounding

我有一个函数做一些数学计算并返回一个double.由于std::exp实现方式不同,它在Windows和Android下会得到不同的结果(为什么我得到std :: exp的特定于平台的结果?).e-17舍入差异得到了传播,最终它不仅仅是我得到的舍入差异(结果最终可以改变2.36到2.47).当我将结果与某些预期值进行比较时,我希望此函数在所有平台上返回相同的结果.

所以我需要围绕我的结果.这样做的最简单的解决方案显然是(据我在网上找到)std::ceil(d*std::pow<double>(10,precision))/std::pow<double>(10,precision).但是,根据平台的不同,我觉得这仍然会得到不同的结果(此外,很难确定precision应该是什么).

我想知道是否硬编码最不重要的字节double可能是一个很好的舍入策略.

这个快速测试似乎表明"是":

#include <iostream>
#include <iomanip>

double roundByCast( double d )
{
    double rounded = d;
    unsigned char* temp = (unsigned char*) &rounded;
    // changing least significant byte to be always the same
    temp[0] = 128;
    return rounded;
}

void showRoundInfo( double d, double rounded )
{
    double diff = std::abs(d-rounded);
    std::cout << "cast: " << d << " rounded to " << rounded << " (diff=" << diff << ")" << std::endl;
}

void roundIt( double d )
{
    showRoundInfo( d, roundByCast(d) );
}

int main( int argc, char* argv[] )
{
    roundIt( 7.87234042553191493141184764681 );
    roundIt( 0.000000000000000000000184764681 );
    roundIt( 78723404.2553191493141184764681 );
}
Run Code Online (Sandbox Code Playgroud)

这输出:

cast: 7.87234 rounded to 7.87234 (diff=2.66454e-14)
cast: 1.84765e-22 rounded to 1.84765e-22 (diff=9.87415e-37)
cast: 7.87234e+07 rounded to 7.87234e+07 (diff=4.47035e-07)
Run Code Online (Sandbox Code Playgroud)

我的问题是:

  • unsigned char* temp = (unsigned char*) &rounded安全还是这里存在未定义的行为,为什么?
  • 如果没有UB(或者如果没有UB有更好的方法可以做到这一点),这样的圆函数对所有输入都是安全和准确的吗?

注意:我知道浮点数不准确.请不要将浮点数学的重复标记为重复吗?或者为什么浮点数不准确?.我理解为什么结果不同,我只是想找到一种方法让它们在所有目标平台上都相同.


编辑,我可能会重新提出我的问题,因为人们会问为什么我有不同的价值观以及为什么我希望它们是相同的.

假设您double从计算中得到一个可能因平台特定实现(例如std::exp)而导致不同值的计算结果.如果你想修复那些不同的东西double,最终在所有平台上拥有完全相同的内存表示(1),并且你想要尽可能地减少最少的精度,那么,修复最不重要的字节是一个好方法吗?(因为我觉得四舍五入到任意给定的精度可能会比这个技巧丢失更多的信息).

(1)通过"相同表示",我的意思是如果将其转换为a std::bitset,则希望看到所有平台的相同位序列.

Ben*_*igt 5

不,舍入不是用于消除小错误或保证与错误执行的计算一致的策略.

对于将数字线切割成范围的任何切片,您将成功消除大多数轻微的偏差(通过将它们放在同一个桶中并夹到相同的值),但如果原始值对跨越边界,则会大大增加偏差.

在您的特定情况下,硬编码最低有效字节,非常接近的值

0x1.mmmmmmm100
Run Code Online (Sandbox Code Playgroud)

0x1.mmmmmmm0ff
Run Code Online (Sandbox Code Playgroud)

只有一个ULP的偏差...但在你的四舍五入后,它们相差256 ULP.哎呀!