为什么将double转换为int可能会得出不同的结果?

时间:2019-05-16 16:49:09

标签: c gcc type-conversion

我正在使用固定的小数点数字(使用uint16_t)存储带有2个小数位的百分比。我发现我将双精度值转换为整数的方式在结果值上有所不同。

<messageText heading="Temporary Maintenance message">**This is a sample message** <accessibilityText>close this message</accessibilityText></messageText>

示例输出:

const char* testString = "99.85";
double percent = atof(testString);
double hundred = 100;
uint16_t reInt1 = (uint16_t)(hundred * percent);
double stagedDouble = hundred * percent;
uint16_t reInt2 = (uint16_t)stagedDouble;

在0到10000(定点表示)之间的所有值中,大约47%可见该错误。使用percent: 99.850000 stagedDouble: 9985.000000 reInt1: 9984 reInt2: 9985 进行投射时,它根本不会出现。而且我不明白为什么两个整数不同。我正在使用GCC 6.3.0。

  

编辑:   改进了代码段,以演示stagedDouble变量并统一两个语句之间的系数。将percent更改为100似乎是一种质量变化,可能会影响输出,但不会改变程序中的任何内容。

2 个答案:

答案 0 :(得分:2)

percent是浮点数吗?如果是这样,请查看您要乘法的类型。

reInt1double * float,而stagedDoubleint * float。混淆浮点数学运算会导致这些类型的舍入错误。

将100更改为double或全部int都会得到相同的答案。

答案 1 :(得分:2)

报告的行为与percent被声明为float以及float和{{使用IEEE-754基本32位和64位二进制浮点是一致的。 1}}。

double

由于uint16_t reInt1 = (uint16_t)(100.0 * percent); 是一个100.0常量,因此它将double转换为percent,在double中执行乘法,并将结果转换为{{1 }}。乘法可能会产生很小的舍入误差,最大为双精度格式的½ULP,相对误差约为2 −53

double

由于uint16_t是一个double stagedDouble = 100 * percent; uint16_t reInt2 = (uint16_t)stagedDouble; 常量,因此它将100转换为int,在100中执行乘法,并将结果转换为{{1 }}。乘法中的舍入误差可能高达float格式的½ULP,相对误差约为2 −24

由于所有值都接近整数的百分之一,因此up:down的错误比率为50:50时,大约一半的结果将低于整数阈值所需的值。在乘法中,所有值分别为0、25、50或100的百分之一将是精确的(因为25/100是¼,这在二进制浮点数中可精确表示),因此96/100将具有舍入错误。如果floatuint16_t舍入误差的方向表现为独立的,统一的随机变量,则大约一半会在不同方向上舍入,从而产生不同的结果,从而导致48%的不匹配,这与47在问题中报告的百分比。

(但是,当我测量实际结果时,我发现floatfloat方法之间有42%的差异。我怀疑这与{{1}中的尾随位有关}在四舍五入之前进行乘法运算-分布可能不像两种可能性的均匀分布。可能是OP的代码以某种方式准备了double值,而不是将整数值除以100。)