gpt4 book ai didi

c++ - 将浮点除法分解为整数和小数部分

转载 作者:塔克拉玛干 更新时间:2023-11-03 01:41:34 25 4
gpt4 key购买 nike

我正在尝试使用 double 进行整数除法 + 模运算(用于基于样条的插值),但是在使用 std::floorstd::fmod.

我一直在使用下面的 div1 的等价物,但是在 50 时它产生了不正确的结果(也就是说,整数部分是 3,但模数部分是除数减去 epsilon)。 div2 有效但相当复杂。 div3 至少是一致的,但没有返回我想要的结果类型(余数可能是负数,所以在我可以使用它之前需要进一步的操作)。

#include <iostream>
#include <cmath>

std::pair<double, double> div1(int num, double denom){
double whole = std::floor(num / denom);
double remain = std::fmod(num, denom);
return {whole, remain};
}
std::pair<double, double> div2(int num, double denom){
double floatdiv = num / denom;
double whole;
double remain = std::modf(floatdiv, &whole);
return {whole, remain * denom};
}
std::pair<double, double> div3(int num, double denom){
double whole = std::round(num / denom);
double remain = std::remainder(num, denom);
return {whole, remain};
}
int main() {
double denom = 100.0 / 6;
int divtype = 0;
for(auto div: {div1, div2, div3}){
std::cerr << "== Using div" << ++divtype << " for this run ==\n";
for(int i = 40; i <= 60; ++i){
auto res = div(i, denom);
std::cerr << i << ": " << res.first << ", " << res.second << " = " << res.first * denom + res.second << "\n";
}
auto oldprec = std::cerr.precision(64);
auto res = div(50, denom);
std::cerr << 50 << ": " << res.first << ", " << res.second << " = " << res.first << " * " << denom << " + " << res.second << " = " << std::floor(res.first) * denom + res.second << "\n";
std::cerr.precision(oldprec);
std::cerr << "\n";
}
return 0;
}

https://ideone.com/9UbHcE

对于 50 的情况,产生以下结果:

 - div1: 3, 16.6666... 
- div2: 3, 0
- div3: 3, -3e-15

是我做错了什么,还是 std::floor(num/denom) + std::fmod(num, denom) 不可靠?如果是这样,什么是好的替代品? div2 是最佳选择吗?

包含最多答案的代码示例版本: https://ideone.com/l2wGRj

最佳答案

你的核心问题是denom = 100.0/6与数学上的精确值不同 denomMath = 100/6 = 50/3 ,因为它不能表示为二的幂之和。我们可以写denom = denomMath + eps (带有一个小的正或负 epsilon)。分配后,denom与最接近的 float 无法区分!如果您现在尝试除以一些值 denomMath * k = denom * k + eps * k通过 denom , 对于足够大的 k你会得到错误的结果数学(即精确算术) - 在这种情况下你没有希望。这种情况发生的时间取决于所涉及的大小(如果值 < 1,那么所有 div 将产生零的整数部分并且是精确的,而对于大于 2^54 的值,您甚至不能表示奇数)。

但即使在那之前,也不能保证除以 denomMath 的(数学)倍数通过 denom产生的东西可以是 floor编辑或 fmod编到正确的整数。四舍五入可能会让您暂时安全,但如上所示,前提是误差不会太大。

所以:

  • div1遇到此处描述的问题:https://en.cppreference.com/w/cpp/numeric/math/fmod

    The expression x - trunc(x/y)*y may not equal fmod(x,y) when the rounding of x/y to initialize the argument of trunc loses too much precision (example: x = 30.508474576271183309, y = 6.1016949152542370172)

    在你的例子中,50 / denom与确切结果(3,因为 3 - some epsilon 略大于 denom)相比,产生的数字略大 (denomMath)

    你不能依赖 std::floor(num / denom) + std::fmod(num, denom)等于num .

  • div2有上述问题:在你的情况下它有效,但如果你尝试更多的情况,你会发现一个 num / denom稍微太小而不是太大,它也会失败。

  • div3有如上所述的 promise 。它实际上为您提供了您所希望的最精确的结果。

关于c++ - 将浮点除法分解为整数和小数部分,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56580411/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com