超级奇怪的 C++ int 和 float 中的黑洞

发布于 2024-11-03 09:23:22 字数 1243 浏览 1 评论 0原文

基本上我试图在控制台中输入一个值,并将小数点作为整数输出,这就是本质上需要发生的事情。

我开发了一种方法来做到这一点,使用 float、int 和简单的数学。 我对 C++ 还是个新手,但这个错误没有意义。

如果输入 0.01、0.02、0.03、0.04、0.06 或 0.08,则会得到错误的输出。 我基本上想让它像 0.06 * 100 = 6 一样简单。

我很确定这是一个简单的错误,但为什么会这样,显然我正在输入一个完整的浮点数。

#include <iostream>
using namespace std;

int main()
{
    float input = 0;

    while (input <= 0 || input > 999.99)
    {
        cout << "Please enter a number with decimal: ";
        cin >> input;
    }

    int whole_num = input;
    float to_decimal = input - whole_num;
    int decimal = to_decimal * 100;

    cout << decimal << endl;

    return 0;
}

编辑:我找到了问题的解决方案


浮动精度存在问题。到目前为止,在 int 上添加 0.5f 就可以解决这个问题。我知道它可以正确输入 2 位小数,但不确定其他类型。

感谢弗雷德里克·斯莱克曼!

#include <iostream>
using namespace std;

int main ()
{
float asfloat = 0.03;
int asint = asfloat * 100;
int asint_fix = 0.5f + asfloat * 100;
cout << "0.03 * 100 = " << asint << endl;
cout << "0.03 * 100 (with the +0.5f fix) = " << asint_fix << endl;
return 0;
}

返回:

0.03 * 100 = 2
0.03 * 100 (with the +0.5f fix) = 3

Basically im trying to enter a value into the console, and output the decimal point as a whole number, and thats what needs to essentially occur.

I've developed a way to do this, using float, int and simple maths.
I'm still new to C++ but this error is not making sense.

If you enter 0.01, 0.02, 0.03, 0.04, 0.06 or 0.08 you get the wrong output.
I basically want to make it as simple as 0.06 * 100 = 6.

I'm pretty sure its a simple mistake, but why is this so, when clearly I'm entering a whole float number anyway.

#include <iostream>
using namespace std;

int main()
{
    float input = 0;

    while (input <= 0 || input > 999.99)
    {
        cout << "Please enter a number with decimal: ";
        cin >> input;
    }

    int whole_num = input;
    float to_decimal = input - whole_num;
    int decimal = to_decimal * 100;

    cout << decimal << endl;

    return 0;
}

EDIT: I found the solution for my problem


There was a problem with the float accuracy. So far adding 0.5f to the int can fix the problem. I know it does it properly to input of 2 decimal places, not sure for other types.

Thanks to Frederik Slijkerman!

#include <iostream>
using namespace std;

int main ()
{
float asfloat = 0.03;
int asint = asfloat * 100;
int asint_fix = 0.5f + asfloat * 100;
cout << "0.03 * 100 = " << asint << endl;
cout << "0.03 * 100 (with the +0.5f fix) = " << asint_fix << endl;
return 0;
}

Returns:

0.03 * 100 = 2
0.03 * 100 (with the +0.5f fix) = 3

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

二手情话 2024-11-10 09:23:22

这是因为浮点数不能精确地表示十进制数量。

您的计算机使用的浮点数格式是二进制的。这意味着它可以精确地表示 1/2、1/4、1/8、1/16、...及其组合。因此,您可以说 0.5、0.25、甚至 0.75 (0.5 + 0.25),这些都是精确的浮点值。但是,这些分数的组合不能产生 0.01;因此,它的值是近似值。与您测试的其他数字类似的情况。

这是使用二进制浮点的固有限制。这并不是“超级奇怪”;而是“超级奇怪”。这是浮点 101。 :-)

That's because floating point numbers cannot represent decimal quantities exactly.

The floating-point number format your computer uses is binary. That means it can exactly represent 1/2, 1/4, 1/8, 1/16, ..., and combinations thereof. So, you can say 0.5, or 0.25, or even 0.75 (0.5 + 0.25) and those will be exact in floating point. But, 0.01 cannot be created with combinations of those fractions; therefore, its value is approximate. Similar story with the other numbers you tested.

This is an inherent limitation with using binary floating point. It's not "super odd"; this is Floating Point 101. :-)

清音悠歌 2024-11-10 09:23:22

我想在克里斯的回答中补充一点,这是科学计算中错误的典型来源。由于实数无法精确表示(计算机的精度是有限的),因此您会在计算过程中累积舍入误差。当您长时间计算卫星的轨迹时,这是一个非常严重的问题。

因此,存在静态分析工具(例如Astrée)可以帮助您检测此类问题何时会导致问题在您的代码中,或保证您的安全。

所以总而言之,这并不是“非常奇怪”,但肯定是“非常不幸”。

在您的特定情况下,也许使用 double 而不是 float 会有所帮助,它将提高数字二进制表示的精度。

I would add to Chris' answer that this is a classic source of errors in scientific computing. As reals cannot be exactly represented (the precision in a computer is finite), you accumulate rounding errors along the way of your computation. It is a very serious issue when you compute trajectories on a long time, for sattelites for instance.

Thus, there exists static analysis tools (such as Astrée) that help you detect when such problems can cause issues in your code, or guarantees that you're safe.

So all in all, it is not "very odd", but it is certainly "very unfortunate".

In your particular case, maybe using double instead of float can help, it will increase the precision of the binary representation of your number.

风吹雨成花 2024-11-10 09:23:22

我只是想提醒您注意,当您将 float 结果存储到 intint Decimal = to_decimal * 100; 时,您会失去精度。 code> 如果您将其声明为 floatdecimal = to_decimal * 100,那么它应该适合您。

I just want to bring to your notice that you are loosing the precision when you are storing float result into int at line int decimal = to_decimal * 100; If you declare it as float decimal = to_decimal * 100, then it should work for you.

故事灯 2024-11-10 09:23:22

浮点精度的固有问题被 C 和 C++ 中的默认截断舍入放大,例如将 0.999999 舍入到 0。您可以通过舍入而不是截断来使事情更加稳健:

int decimal = 0.5f + to_decimal * 100;

您还可以使用像 1e-6 这样的小值来代替0.5 以获得更稳健的截断。这完全取决于您的具体情况。

The inherent problem with float precision is amplified by the default truncation rounding in C and C++, which rounds e.g. 0.999999 to 0. You can make things more robust by rounding instead of truncating:

int decimal = 0.5f + to_decimal * 100;

You could also use a small value like 1e-6 instead of 0.5 to get a more robust truncation. It all depends on your particular situation.

Bonjour°[大白 2024-11-10 09:23:22

正如其他人提到的,问题在于 C++ 中的浮点数符合 IEEE 754 标准 不幸的是无法准确表示 0.01、0.03 等常见数字。(您可以使用 for (float f=0.0; 等简单循环进行验证) f<=1.0; f+=0.1) printf("%.010f\n",f); 并查看错误累积的速度。)

但是,您通常可以使用整数和除法/乘法来解决此类问题用于输入/输出。另外,GNU GMP 任意精度算术库可能会有所帮助。

As others have mentioned, the problem is that floating point numbers in C++ conform to the IEEE 754 standard which has the unfortunate inability to exactly represent common numbers like 0.01, 0.03, etc. (You can verify with a simple loop like for (float f=0.0; f<=1.0; f+=0.1) printf("%.010f\n",f); and see how quickly the error accumulates.)

However, you can often work around such problems using integers and division/multiplication for input/output. Also, the GNU GMP Arbitrary Precision Arithmetic Library might help.

泪是无色的血 2024-11-10 09:23:22

您需要决定您感兴趣的有效位数(看起来像 2),完成此操作后,这是您可以做的最好的事情:

const int significantBits = 2;
int decimal = (int)(pow(10, significantBits) * div(input, 1.0));

You need to decide how many significant bits you are interested in (it looks like 2), after you've done that, this is the best that you can do.:

const int significantBits = 2;
int decimal = (int)(pow(10, significantBits) * div(input, 1.0));
红颜悴 2024-11-10 09:23:22

阅读浮点指南。这是每个程序员在进行浮点数学运算时都应该了解的内容。

Read the Floating Point Guide. This is what every programmer should know when doing floating-point math.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文