在C中的隐式转换为double。

huangapple go评论56阅读模式
英文:

Implicit conversion to double in C

问题

我正在尝试理解C语言中的隐式转换。我有以下C代码:

#include <stdio.h>
#include <stdlib.h>

int main() {
    int a = -6;
    size_t b = 100;
    int result = a * b; /* a is converted to size_t,
    multiply two unsigned ints and cast the result to int
    */
    double dresult = a * b;
    printf("Result : %d \n", result); //         Output  Result : -600 
    printf("Double Result : %f \n", dresult); // Output  Double Result : 18446744073709551616.000000 
    return 0;
}

根据我从cppreference了解到的,result应该是一些垃圾值(当转换为double时打印出来),但为什么它给我正确的值?

英文:

I am trying to understand implicit conversion in C .I have the following C code:

#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;

int main() {
    int a = -6 ;
    size_t b = 100 ;
    int result = a*b ; /* a is converted to size_t,
    multiply two unsigned ints and cast the result to int
    */
    double dresult = a*b ;
    printf(&quot;Result : %d \n&quot;,result); //         Output  Result : -600 
    printf(&quot;Double Result : %f \n&quot;,dresult); // Output  Double Result : 18446744073709551616.000000 
    return 0;
}

From what I understood from cppreference, the result should be some garbage (as printed when converted to double) value, but why does it give me the right value?

答案1

得分: 4

当评估表达式 a*b 时,将应用通常的算术转换于两个操作数。这意味着:

  • size_t具有等于或高于int的等级的平台上(这在大多数平台上是成立的),a将被转换为size_t。由于size_t是无符号的,a的值(即-6)将被转换为无符号值。在size_t为64位的平台上,这个值将是18446744073709551610。将这个值与b的值(即100)相乘将产生数学结果1844674407370955161000,这在64位的size_t中是不能表示的。因此,假设64位的size_t,实际结果将是数学结果对18446744073709551616(2的64次方)取模的值,即18446744073709551016。在size_t为32位的平台上,相应的结果是4294966696。请注意,这种"溢出"不会引发未定义行为,因为只有有符号整数溢出会引发未定义行为。无符号整数溢出是被明确定义的。

  • size_t等级低于int的平台上(这在理论上是可能的,也被ISO C标准允许,但实际上不太可能存在),a将不会被转换为size_t,所以结果将简单地是-600

基于上述原因,根据您使用的平台不同,a*b的结果可能是184467440737095510164294966696-600

这行代码:

double dresult = a*b;

因此,将在dresult中写入值18446744073709551016.04294966696.0-600.0,具体取决于您使用的平台。在您的平台上,由于浮点不精确性,数值18446744073709551016.0似乎无法精确表示,因此它将四舍五入为18446744073709551616.0

使用以下代码:

int result = a*b;

情况就没有那么简单了。在a*b评估为-600的平台上,很明显值-600将被写入result。然而,在a*b评估为18446744073709551016的平台上,问题在于这个值在int中是无法表示的,假设int是32位的。在这种情况下,根据ISO C11标准的§6.3.1.3 §3,写入result的值是实现定义的,或者会引发一个实现定义的信号。然而,在大多数平台上,结果实际上是通过反复减去目标类型的无符号版本中可表示的最大数值,加上一,直到结果在类型可表示的范围内获得的值。因此,结果将是18446744073709551016 - 18446744073709551616,即-600。这似乎也是您的平台的行为方式。

英文:

When evaluating the expression a*b, the usual arithmetic conversions are applied to both operands. This means that

  • on platforms on which size_t has equal or higher rank than int (which is the case on most platforms), a is converted to size_t. Since size_t is unsigned, the value of a (which is -6) will be converted to an unsigned value. On platforms on which size_t is 64-bit, this value will be 18446744073709551610. Multiplying this value of a with the value of b (which is 100) will yield the mathematical result 1844674407370955161000, which is not representable in a 64-bit size_t. Therefore, assuming a 64-bit size_t, the actual result will be the mathematical result modulo 18446744073709551616 (2 to the power 64), which is 18446744073709551016. On platforms on which size_t is 32-bit, the corresponding result is 4294966696. Note that this "overflow" will not invoke undefined behavior, because only signed integer overflow will invoke undefined behavior. Unsigned integer overflow is well-defined.

  • on platforms on which size_t has a rank less than int (which are theoretically possible and permitted by the ISO C standard, but unlikely to exist in practice), a will not be converted to size_t, so the result will be simply -600.

For the reasons stated above, depending on which platform you are using, the result of a*b will likely be either 18446744073709551016, 4294966696 or -600.

The line

double dresult = a*b ;

will therefore likely write either the value 18446744073709551016.0, 4294966696.0 or -600.0 to dresult, depending on which platform you are using. On your platform, the number 18446744073709551016.0 does not seem to be exactly representable in a double due to floating-point inaccuracy, so that it gets rounded to 18446744073709551616.0.

With the line

int result = a*b ;

the situation is not quite as simple. On platforms on which a*b evaluates to -600, it is clear that the value -600 will be written to result. However, on platforms on which a*b evaluates to 18446744073709551016, the problem is that this value is not representable in an int, assuming that an int is 32-bit. In that case, according to §6.3.1.3 ¶3 of the ISO C11 standard, the value written to result is implementation-defined or an implementation-defined signal is raised. However, on most platforms, the result is simply the value that is obtained by repeatedly subtracting the maximum number representable in the unsigned version of the target type, plus one, until the result is in the range representable by the type. Therefore, the result would be 18446744073709551016 - 18446744073709551616, which is -600. This appears to also be how your platform is behaving.

huangapple
  • 本文由 发表于 2023年5月28日 02:44:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/76348485.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定