gpt4 book ai didi

c++ - 为什么返回浮点值会改变它的值?

转载 作者:IT老高 更新时间:2023-10-28 21:49:27 25 4
gpt4 key购买 nike

以下代码在 Red Hat 5.4 32 位上引发 assert,但在 Red Hat 5.4 64 位(或 CentOS)上有效。

在 32 位上,我必须把 millis2seconds 的返回值放在一个变量中,否则 assert 被引发,表明 double< 的值 从函数返回的与传递给它的不同。

如果您评论“#define BUG”行,它会起作用。

感谢@R,将 -msse2 -mfpmath 选项传递给编译器使 millis2seconds 函数的两个变体都可以工作。

/*
* TestDouble.cpp
*/

#include <assert.h>
#include <stdint.h>
#include <stdio.h>

static double millis2seconds(int millis) {
#define BUG
#ifdef BUG
// following is not working on 32 bits architectures for any values of millis
// on 64 bits architecture, it works
return (double)(millis) / 1000.0;
#else
// on 32 bits architectures, we must do the operation in 2 steps ?!? ...
// 1- compute a result in a local variable, and 2- return the local variable
// why? somebody can explains?
double result = (double)(millis) / 1000.0;
return result;
#endif
}

static void testMillis2seconds() {
int millis = 10;
double seconds = millis2seconds(millis);

printf("millis : %d\n", millis);
printf("seconds : %f\n", seconds);
printf("millis2seconds(millis) : %f\n", millis2seconds(millis));
printf("seconds < millis2seconds(millis) : %d\n", seconds < millis2seconds(millis));
printf("seconds > millis2seconds(millis) : %d\n", seconds > millis2seconds(millis));
printf("seconds == millis2seconds(millis) : %d\n", seconds == millis2seconds(millis));

assert(seconds == millis2seconds(millis));
}

extern int main(int argc, char **argv) {
testMillis2seconds();
}

最佳答案

使用 Linux x86 系统上使用的 cdecl 调用约定,使用 st0 x87 寄存器从函数返回 double 。所有 x87 寄存器都是 80 位精度。使用此代码:

static double millis2seconds(int millis) {
return (double)(millis) / 1000.0;
};

编译器使用 80 位精度计算除法。当 gcc 使用标准的 GNU 方言(默认情况下)时,它将结果留在 st0 寄存器中,因此将完整的精度返回给调用者。汇编代码的结尾是这样的:

fdivrp  %st, %st(1)  # Divide st0 by st1 and store the result in st0
leave
ret # Return

使用此代码,

static double millis2seconds(int millis) {
double result = (double)(millis) / 1000.0;
return result;
}

结果存储到 64 位内存位置,这会丢失一些精度。 64位的值在返回之前被加载回80位的st0寄存器,但是损坏已经造成:

fdivrp  %st, %st(1)   # Divide st0 by st1 and store the result in st0
fstpl -8(%ebp) # Store st0 onto the stack
fldl -8(%ebp) # Load st0 back from the stack
leave
ret # Return

在您的 main 中,第一个结果存储在 64 位内存位置,因此无论哪种方式都会丢失额外的精度:

double seconds = millis2seconds(millis);

但是在第二次调用中,直接使用了返回值,所以编译器可以将其保存在寄存器中:

assert(seconds == millis2seconds(millis));

当使用 millis2seconds 的第一个版本时,您最终会将截断为 64 位精度的值与完全 80 位精度的值进行比较,因此存在差异。

在 x86-64 上,计算是使用只有 64 位的 SSE 寄存器完成的,因此不会出现此问题。

另外,如果你使用 -std=c99 这样你就不会得到 GNU 方言,计算的值会存储在内存中并在返回之前重新加载到寄存器中,以便符合标准。

关于c++ - 为什么返回浮点值会改变它的值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16888621/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com