为什么GCC会警告这种隐式转换?

10

GCC警告我以下代码段包含可能会改变值的隐式转换:

#include <stdlib.h>
float square = rand();

然而,以下代码并未产生任何警告:

float square = 100;

GCC 给出的警告如下:

tests/ChemTests.cpp:17:23: error: conversion to ‘float’ from ‘int’ may alter its value

我不明白为什么第一行会给出一个警告,因为rand()已经被正确声明并返回一个int,就像100整数字面量一样。

为什么第一行会给出编译器警告而第二行却不会,即使两者都有从intfloat的隐式转换?


1
我发现除非我使用“-Wconversion”选项,否则我不会收到警告。 - Keith Thompson
3个回答

15

GCC在类型转换可能导致精度损失时会发出此警告。(换句话说,值可能被“改变”)

在第一种情况中,rand()返回一个int。由于不能将可以存储在int中的所有值表示为float,因此它会发出此警告。

在第二种情况中,可以将100安全地转换为float,而不会有任何精度损失。


这是否意味着 float f = 123456789 会出错? - Brian L
它不会出错,但应该会发出警告。我此刻没有GCC,但我刚在Visual Studio中进行了测试,它会发出警告:warning C4305: 'initializing' : truncation from 'int' to 'float' - Mysticial

6
为了补充Mysticial所写的内容(正确的):你的C语言实现使用32位IEEE 754单精度二进制浮点数作为float,并且使用32位整数作为int。在“你的”int中,你可以有31位数字和1位符号。在“你的”float中,mantissa是24位,并且有1位符号。显然,需要超过24位加上符号才能表示的int无法精确转换为“你的”float。(我使用“你的”来代表你正在使用的编译器。C标准没有规定floatint的确切长度。)
现在,rand()可以生成任何int数,所以编译器必须给出警告。100是一个在编译时已知的数值字面量,因此编译器可以静态检查该数字是否可转换。
即使不解释浮点数的工作原理,您的int是32位且仅支持整数。您的float是32位并支持浮点数。显然,浮点数更难表示(您必须在某个地方保存小数点),因此如果int和float具有相同的长度,则必须支付一定的“代价”。这个代价就是精度。
回应您的评论,您可以在与0“连续”的float中准确表示的最大数字为16777215(其尾数= 16777215,指数= 0)和16777216(其尾数= 1,指数= 24,因为它是1 * 2 ^ 24)。 16777217无法准确表示。16777218可以。

0

并非每个int都可以表示为float。具体来说,如果在int中最高位和最低位之间的位数大于<float.h>中定义的FLT_MANT_DIG - 1,则无法精确地表示为float。(对于doubleDBL_MANT_DIG - 1也是如此。)编译器会警告您存在精度损失的潜在风险,因为rand()的声明意味着rand()可能返回任何int,包括那些无法表示为float的值。

gcc应该足够聪明,知道何时可以精确地表示int字面量:

float f= 1<<FLT_MANT_DIG; // yes
float g= (1<<FLT_MANT_DIG) - 1; // yes
float h= (1<<FLT_MANT_DIG) + 1; // no
float i= (1<<(FLT_MANT_DIG + 1)); // yes

在初始化h时,gcc应该会发出警告。

顺便说一句,如果RAND_MAX小于或等于(1<<FLT_MANT_DIG) - 1,即使编译器向你抱怨,你也可以安全地将rand()分配给float


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接