以下程序。
#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 int
到float
的转换发生了这个错误,但我不知道有关如何创建奇怪结果的详细信息。
答案 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位移动到x
。x
现在有了 值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
表示指数0
,1000000 = 128
代表1,01111110 = 126
代表-1
,等等。尾数适合剩余的24
位 它的前导1
如上所述被剥离。 Source
正如您所看到的,在转换4294965296
到float
时,会发生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.0
为double
类型,因此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