【问题标题】:Math Error with Primitive Operators原始运算符的数学错误
【发布时间】:2016-02-28 12:16:44
【问题描述】:

我在使用内置运算符时遇到了原始类型的问题。我所有的运算符都适用于除float(un)signed long long int 之外的所有数据类型。

为什么即使乘以一也会出错?另外,为什么+10-10 给出的数字与+1-1/1*1 相同。

之所以选择数字461168601,是因为它符合最大float 和最大signed long long int

运行以下代码并得到以下输出:

fmax  : 340282346638528859811704183484516925440
imax  : 9223372036854775807
i     : 461168601
f     : 10
f2    : 1

461168601 / 10 = 46116860
461168601 + 10 = 461168608
461168601 - 10 = 461168608

461168601 * 1 = 461168608
461168601 / 1 = 461168608
461168601 + 1 = 461168608
461168601 - 1 = 461168608

下面的代码可以运行here

#include <iostream>
#include <sstream>
#include <iomanip>
#include <limits>

#define fmax std::numeric_limits<float>::max()
#define imax std::numeric_limits<signed long long int>::max()

int main()
{

    signed long long int i    = 461168601;
    float f = 10;
    float f2 = 1;
    std::cout << std::setprecision(40);
    std::cout <<"fmax  : " << fmax  << std::endl;
    std::cout <<"imax  : " << imax  << std::endl;
    std::cout <<"i     : " << i    << std::endl;
    std::cout <<"f     : " << f    << std::endl;
    std::cout <<"f2    : " << f2   << std::endl;
    std::cout <<std::endl;
    std::cout << i << " / " << f << " = " << i / f << std::endl;
    std::cout << i << " + " << f << " = " << i + f << std::endl;
    std::cout << i << " - " << f << " = " << i - f << std::endl;
    std::cout <<std::endl;
    std::cout << i << " * " << f2 << " = " <<i * f2 << std::endl;
    std::cout << i << " / " << f2 << " = " << i / f2 << std::endl;
    std::cout << i << " + " << f2 << " = " << i + f2 << std::endl;
    std::cout << i << " - " << f2 << " = " << i - f2 << std::endl;
}

【问题讨论】:

  • 我的问题不是关于类型转换,而是不正确的结果。
  • 你为什么打电话给2**63-1FLT_MAX?这远小于浮点数的最大值,也远大于浮点数的最大精度。您的“错误”答案包括将2**62-1 转换为float,从而导致2**62 2**62-1 没有浮点表示,但2**62 有浮点表示
  • @TMKelleher 你问为什么 unsigned long long int 会被转换为浮点数。
  • 你犯了两个错误:第一个涉及隐式类型转换(4611686018427387903 在任何操作之前首先转换为float),第二个涉及值存储到浮点数的方式 - 不像整数类型,如果浮点数可以准确存储X 的值,并不意味着它可以准确存储0X 之间的任何整数值。不可能将4611686018427387903 完全存储在float 中,因此您会得到最接近的值4611686018427387904,这就是您的* 1 操作错误的原因(其他操作的推理相同)。

标签: c++ casting operators unsigned-long-long-int


【解决方案1】:

该错误是由4611686018427387904110 之间的差异太大引起的。您永远不应该对具有这种差异的数字求和,因为两个最接近的浮点数之间的实际差异会随着指数值的增长而增长。

当两个浮点数相加时,第一个它们与相同的指数值(较大的)对齐,所以在操作之前你有例如1e101e-10 对齐后你有 1e100e10 结果是 1e10

【讨论】:

  • 如果我理解你的话,第一个数字应该变成 4.611686018427387904E1810 变成 0.000000000000000001E18 如果数字足够小,最终可能会变成 0。这意味着4611686018427387904 + 1 应该等于4611686018427387904,因为1 由于精度而丢失。唉,事实并非如此。
  • @TMKelleher 即使转换为浮动也可能导致问题。尝试输出:(double) (461168601)
  • 这是有道理的,但是当我使用数字230584000 而转换为float 返回相同的数字时,为什么会遇到同样的问题? 230584000 + 10 = 230584016230584000 - 10 = 230583984230584000 * 1 = 230584000230584000 / 1 = 230584000230584000 + 1 = 230584000230584000 - 1 = 230584000
【解决方案2】:

挖了一些,发现this article

施法会打开自己的蠕虫罐头。您必须小心,因为您的浮点数可能没有足够的精度来保存整个整数。 32 位整数可以表示任何 9 位十进制数,但 32 位浮点数只能提供大约 7 位的精度。因此,如果您有大整数,则进行此转换会破坏它们。值得庆幸的是,双精度数具有足够的精度来保存整个 32 位整数(再次注意浮点精度和整数动态范围之间的类比)。此外,数字类型之间的转换、从浮点数到整数或浮点数和双精度数之间的转换也会产生一些开销。

因此,基本上,一旦一个数字的整个部分达到大约七位数,float 就会开始移动数字以保持该数字的整个部分大约七位数。当发生这种小数位移动时,数字开始达到浮点不准确。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-04
    • 2016-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多