在 c++ 中将双精度转换为整数损失 1

Convert double to int lose 1 in c++

本文关键字:整数 损失 转换 双精度 c++      更新时间:2023-10-16

可能的重复项:
为什么这种双倍到整数的转换不起作用?

在 c++ 中将双精度转换为 int lose 1

#include <iostream>
#include <cmath>
using namespace std;
void main() {
    double num = 1234.34;
    int numInt = num;
    double numAfterPoint = num - numInt; // 0.34
    int counter = 1;
    double numFloatPower = numAfterPoint;
    while (true) {
        numFloatPower = numAfterPoint * pow(10.0, counter);
        cout << numFloatPower << " > " << (int)numFloatPower << " ";
        system("pause");
        counter++;
    }
}

当前结果:

3.4 > 3 Press any key to continue . . .
34 > 33 Press any key to continue . . .
340 > 339 Press any key to continue . . .
3400 > 3399 Press any key to continue . . .

结果应为:

3.4 > 3 Press any key to continue . . .
34 > 34 Press any key to continue . . .
340 > 340 Press any key to continue . . .
3400 > 3400 Press any key to continue . . .

等。。。

cast-to-int 操作(int)doubleValue执行截断,这意味着如果数字在内部表示为 33.9999999999...,它将被截断为 33。

如果需要执行舍入(3.9 → 4、3.4 → 3(,请使用 round()


注意:

    1234.34
  • 实际上是 1234.33999999999991814547684044...因为它不能用有限数量的二进制数字精确地表示,所以在第 53 位它将被四舍五入
  • 因此,您的 0.34
  • 实际上是 0.33999999999991814547684044...,
  • 您的 3.4 实际上是 3.3999999999991814547684044...,
  • 你的 34 实际上是 33.999999999991814547684044...,
  • 您的 340 实际上是 339.99999999991814547684044...,
  • 等。

浮点运算使用2的有限幂和来表示任意数字。像3.4这样的数字,不完全是2的幂之和,可以四舍五入。在这种情况下,它略微向下舍入,可以说3.39996…。然后,当您相乘时,结果将是 33.9996… ,根据舍入到零的规则,该结果将向下舍入为 33

C++控制台 I/O 足够智能,可以四舍五入到最接近的偶数,即显示多少位十进制数字,但计算机的内部数学电路对此一无所知。它使用值的完全精度,在本例中包含错误。

试着cout << setprecision( 20 );看到丑陋的真相。

问题是你的第一个假设是错误的:

double num = 1234.34;
int numInt = num;
double numAfterPoint = num - numInt; // 0.34

std::cout << std::setprecision(17) << numAfterPoint << "n";
3.3999999999991815

现在,如果您在主循环中设置精度,您将获得以下数字。

3.3999999999991815 > 3 
33.999999999991815 > 33 
339.99999999991815 > 339 
3399.9999999991815 > 3399

麻烦的是流打印代码在打印之前对结果进行舍入,导致它打印3.4而不是3.3999999999。这是因为 3.4 不能用二进制浮点数精确表示。

为了帮助你理解发生了什么,让我给你一个非常简单的解释,使用固定的小数位数来解释类似的问题。(您遇到的问题是由于固定的二进制位置造成的,这更难理解,但问题是一样的。

你能做的最好的代表1/3就是.333333 .但现在3 * 1/3 != 1.

您可以将2/3表示为 .666666 ,然后2 * 1/3 = 1/3,但随后2/3 + 1/3 != 1

你可以表示2/3.666667 ,然后表示 2/3 + 1/3 = 1 ,但随后
2/3 - 1/3 - 1/3 != 02 * 1/3 != 2/3

由于舍入和有限的精度,您不应期望浮点数学生成完全正确的结果。

正如 1/3 没有精确的十进制表示形式一样,.34 也没有精确的二进制表示形式。

由于十进制数通常不能完全以二进制表示,因此num不是 1234.34,而是最接近该数字的double值。那个可能略小于 1234.34(例如,1234.339999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999在这种情况下,当然numAfterPoint也略小于 0.34,使得 100*numAfterPoints 明显小于 34 等。由于转换为 int 会删除小数部分,即使它非常接近 1,您也会得到 33 而不是 34。

另一方面,如果直接输出浮点值,则会将其舍入到特定的位数(您可以使用流操纵器进行控制(。因此,除非您告诉流输出非常多的数字,否则您看不到差异。尝试

std::cout << std::setprecision(20) << numAfterPoint << 'n';

看看真正存储了什么价值。