将unsigned int转换为float时出错

时间:2018-06-14 14:12:35

标签: c

以下程序。

#include <stdio.h>

int main()
{
    unsigned int a = 10;
    unsigned int b = 20;
    unsigned int c = 30;
    float d = -((a*b)*(c/3));
    printf("d = %f\n", d);
    return 0;
}

输出

非常奇怪
d = 4294965248.000000

当我更改表达式中的幻数3以计算d到3.0时,我得到了正确的结果:

d = 2000.000000

如果我将a,b,c的类型更改为int,我也会得到正确的结果。

我想从unsigned intfloat的转换发生了这个错误,但我不知道有关如何创建奇怪结果的详细信息。

6 个答案:

答案 0 :(得分:2)

我认为你意识到在分配到unsigned int之前,你将减去float。如果您运行以下代码,则很可能会4294965296

#include <stdio.h>

int main()
{
    unsigned int a = 10;
    unsigned int b = 20;
    unsigned int c = 30;
    printf("%u", -((a*b)*(c/3)));

    return 0;
}
  

等号右侧的-2000设置为已签名   整数(大小可能是32位)并具有十六进制值   0xFFFFF830。编译器生成用于移动此有符号整数的代码   到无符号整数x,它也是一个32位实体。该   编译器假设您只有一个正值   等号,所以它只是将所有32位移动到xx现在有了   值0xFFFFF830如果被解释为正数,则为4294965296   数。但printf格式%d表示32位是   解释为有符号整数,因此得到-2000。如果你曾经使用过   %u它会打印为4294965296

#include <stdio.h>
#include <limits.h>

int main()
{
    float d = 4294965296;
    printf("d = %f\n\n", d);
    return 0;
}

4294965296转换为float时,您使用的数字很长,以适应小数部分。现在已经失去了一些精确度。由于损失,我得到了4294965248.000000

  

IEEE-754浮点标准是表示的标准   并操纵所有人遵循的浮点数量   现代计算机系统。

bit  31 30    23 22                    0
     S  EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM
     

位数从最低有效位开始计数。首先   bit是符号(0表示正数,1表示负数。下列   8位是多余-127 二进制表示法中的指数;这个   表示二进制模式01111111 = 127表示指数   01000000 = 128代表1,01111110 = 126代表   -1,等等。尾数适合剩余的24位   它的前导1如上所述被剥离。 Source

正如您所看到的,在转换4294965296float时,会发生00011000丢失的精度。

11111111111111111111100 00011000 0  <-- 4294965296
11111111111111111111100 00000000 0  <-- 4294965248

答案 1 :(得分:1)

您的整个计算将完成unsigned,因此它与

相同
 float d = -(2000u);
-2000中的{p> unsigned int(假设32位int)为4294965295

这会写在float d中。但由于float无法保存此确切数字,因此会将其保存为4294965248

根据经验,你可以说浮点精度为7个显着基数10位数。

计算的是2^32 - 2000,然后其余的浮点精度。

如果您改为使用3.0,则会更改计算中的类型,如下所示

float d = -((a*b)*(c/3.0));
float d = -((unsigned*unsigned)*(unsigned/double));
float d = -((unsigned)*(double));
float d = -(double);

给你正确的负值。

答案 2 :(得分:1)

这是因为您在-上使用unsigned int-反转了数字的位。让我们打印一些无符号整数:

printf("Positive: %u\n", 2000);
printf("Negative: %u\n", -2000);

// Output:
// Positive: 2000
// Negative: 4294965296

让我们打印十六进制值:

printf("Positive: %x\n", 2000);
printf("Negative: %x\n", -2000);

// Output
// Positive: 7d0
// Negative: fffff830

如您所见,这些位是反转的。所以问题来自在-上使用unsigned int,而不是从unsigned int转换为浮动。

答案 3 :(得分:1)

正如其他人所说,问题是你试图否定一个无符号数。已经给出的大多数解决方案都使用某种形式的浮动来浮动,以便算术在浮点类型上完成。另一种解决方案是将算术结果转换为int然后否定,这样算术操作将在整数类型上完成,这可能是也可能不是优选的,具体取决于您的实际用例:

#include <stdio.h>

int main(void)
{
    unsigned int a = 10;
    unsigned int b = 20;
    unsigned int c = 30;
    float d = -(int)((a*b)*(c/3));
    printf("d = %f\n", d);
    return 0;
}

答案 4 :(得分:0)

-((a*b)*(c/3));全部在无符号整数算术中执行,包括一元否定。对于无符号类型,一元否定是明确定义的:数学上结果是模2 N 其中N是unsigned int中的位数。当您将大号分配给float时,会遇到一些精度损失;由于其二进制幅度,结果是与除{20}之间的unsigned int最接近的数字。

如果您将3更改为3.0,则c / 3.0double类型,因此a * b的结果会在乘以之前转换为double。然后将此double分配给float,并且已经观察到精度损失。

答案 5 :(得分:0)

你需要将注册内容转换为浮点数

docker-php-ext-install

apt-get install -y libxml2-dev php-soap && apt-get clean -y && docker-php-ext-install soap