为什么将两个整数相除并赋值给双精度浮点数时,结果不正确?

135

在下面的代码片段中,为什么

int a = 7;
int b = 3;
double c = 0;
c = a / b;

c的值最终为2,而不是预期的2.3333。如果ab是双精度浮点数,则答案会变成2.333。但是,既然c已经是一个双精度浮点数,那么它难道不应该可以使用整数吗?

那么为什么int/int=double不起作用呢?


可能是除法结果始终为零的重复问题。 - phuclv
10个回答

194

这是因为您正在使用整数除法版本的 operator/,它接受两个 int 并返回一个 int。 要使用返回 doubledouble 版本中的至少一个,int 必须明确转换为 double

c = a/(double)b;

11
为了更加清晰易懂,我希望将ab都明确地转换成double类型,但其实这并不重要。 - John Dibling
37
由于问题标记了C ++,我更喜欢看到使用static_cast<>而不是C样式转换。 - Martin York
19
个人认为,C语言风格的强制类型转换更加清晰明了(大多数其他常见编程语言中的强制类型转换也是采用C风格的方式)。static_cast<>对我来说总是显得拖沓冗长。在原始数据类型的情况下,很难将static_cast<>reinterpret_cast<>混淆。 - Chad La Guardia
8
对于算术类型的转换?在这种情况下,我更倾向于避免使用static_cast,而是使用C风格的转换。在这里使用C++风格的转换没有任何好处,只会使代码变得更加混乱。算术类型的转换正是C风格转换非常适合的情境,并且比其他转换更为恰当。 - AnT stands with Russia
23
有时候你可以通过写 double(b) 来瞒过那些反对使用 C 风格类型转换的人。他们并不总是能意识到这是一种转换,因为它看起来和一个显式构造函数调用一样。 - Steve Jessop
将一个 int 强制转换为 double 可以正常工作。但是为什么呢?我对用户定义类型感到困惑。当我们重载运算符时,左变量(对象)决定调用哪个类的运算符。INT obj; obj/3; 这里 / 是针对 obj 类调用的,而不是针对 3。 - Asif Mushtaq

15

这里是:

a) 两个 int 相除总是执行整数除法。因此,在您的情况下,a/b 的结果只能是一个 int

如果您想保持 abint,但又要完全除以它们,您必须将它们中的至少一个强制转换为 double: (double)a/ba/(double)b(double)a/(double)b

b) c 是一个 double,所以它可以在赋值时接受一个 int 值:将自动将 int 转换为 double 并分配给 c

c) 请记住,在赋值时,等号右侧的表达式首先计算(根据规则 a 上述,而不考虑等号左侧的变量),然后赋值给等号左侧的变量(根据规则 b 上述)。我相信这完成了整个过程。


14

除非特别说明(我只能想到一种情况),C++会从表达式本身完全确定表达式(或子表达式)的含义。您对表达式结果的操作并不重要。

在您的情况下,表达式a/b中没有看到任何double;所有内容都是int。因此,编译器使用整数除法。只有在获得结果后,它才考虑如何处理结果,并将其转换为double


3
我能想到的唯一例外是在使用指针时选择函数重载——&funcname 的值取决于你将其强制转换为哪种类型。 - Steve Jessop
2
@Steve Jessop 这也是我能想到的唯一例外情况。(但考虑到标准的规模和复杂性,我不敢保证我没有漏掉任何内容。) - James Kanze

9
在C++语言中,子表达式的结果从不受周围上下文的影响(有一些罕见的例外情况)。这是语言小心遵循的原则之一。表达式c = a / b包含一个独立的子表达式a / b,它独立于该子表达式之外的任何内容进行解释。语言不关心您稍后将结果分配给doublea / b是整数除法。其他任何事情都无关紧要。您将看到这个原则在语言规范的许多角落中得到了遵循。这就是C++(和C)的工作方式。
我上面提到的一个例外是函数重载情况下的函数指针赋值/初始化。
void foo(int);
void foo(double);

void (*p)(double) = &foo; // automatically selects `foo(fouble)`

这是一种情况,左侧的赋值/初始化会影响右侧的行为。(此外,对数组的引用初始化可以防止数组类型衰减,这是类似行为的另一个例子。)在所有其他情况下,右侧完全忽略左侧。


8
当你将两个整数相除时,结果将是一个整数,无论你是否将其存储在 double 类型中。

7

c是一个double类型的变量,但它被赋予的值是一个int类型的数值,因为它来自两个int类型数值的除法运算,这会产生“整数除法”(舍弃余数)。所以在代码行c=a/b中发生了以下步骤:

  1. 计算出a/b,并创建一个类型为int的临时变量。
  2. 将临时变量的值转换成double类型,然后赋值给c

值得注意的是,a/b的值是在没有考虑上下文的情况下确定的(即没有考虑它被赋值给double类型的变量)。


5
< p > / 运算符可用于整数除法或浮点除法。由于您提供了两个整数操作数,因此它执行整数除法,然后将结果存储在双精度变量中。< /p >

3
这个问题从技术上来说与语言有关,但几乎所有语言都会处理此类情况。当表达式中两个数据类型不匹配时,大多数语言将尝试根据一组预定义规则将“=”两边的数据类型进行转换以匹配另一边的数据类型。
当两个相同类型的数字相除(整数、浮点数等)时,结果将始终是相同类型的(因此'int/int'将始终得到int)。
在这种情况下,你有 double var = integer result 这将在计算之后将integer result强制转换为double,此时小数部分的数据已经丢失了。 (大多数语言将执行此转换以防止类型不准确而不引发异常或错误)。
如果想保留结果作为double,您需要创建一个这样的情况: double var = double result
最简单的方法是强制右侧表达式转换为double:
c = a/(double)b
整数和浮点数之间的除法将导致将整数转换为浮点数(请注意,在执行数学运算时,编译器通常会“向上转换”到最具体的数据类型,以防止数据丢失)。
在向上转换之后,a将成为double,现在你有了两个double相除的情况。 这将创建所需的除法和赋值。
再次强调,这是特定于语言的(甚至可以特定于编译器),但几乎所有语言(我能想到的所有语言)都会以相同的方式处理此示例。

这个问题被标记为[C++],C++标准规定了它的工作方式。不确定你所说的“语言特定”是什么意思,但假设没有启用编译器扩展,它肯定不是编译器特定的。 - John Dibling
同时,说“double var = integer result which casts the double var down to int”是不正确的。double并没有被强制转换成int。相反,int结果被转换为double。 - John Dibling
我考虑到编译器扩展的可能性(实际上我曾经遇到过这样的问题,我的环境“错误地”转换了结果,我无法弄清原因)。而且结果是特定于语言的,因为有些语言不遵循相同的转换规则。我没有考虑到这是一个特定于C++的标签。你说得对,“double var = integer result”的评论。已编辑以反映这一点。谢谢! - matthewdunnam

2

出于上述原因,您需要将'a'或'b'之一转换为double类型。另一种方法是使用:

double c = (a+0.0)/b;

分子被(隐式)转换为double,因为我们已经加上了一个double,即0.0


1
重要的是计算中的一个元素必须是浮点-双精度类型。为了获得双精度结果,您需要像下面所示进行强制转换:
c = static_cast<double>(a) / b;

或者你可以直接创建它:

或者使用 static_cast 进行类型转换:

c = 7.0 / 3;

注意,计算中的一个元素必须带有“.0”以表示浮点双精度类型除以整数。否则,即使c变量是double类型,结果也将为零(整数)。

你的回答有什么独特之处,其他9个答案都没有呈现出来的吗? - bolov

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