![](/img/trans.png)
[英]Why storing a double expression in a variable before a cast to int can lead to different results than casting it directly?
[英]Why casting double to int might give different results?
我正在使用固定的小数点数字(使用uint16_t)存储带有2个小数位的百分比。 我发现我将双精度值转换为整数的方式会影响结果值。
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;
输出示例:
percent: 99.850000
stagedDouble: 9985.000000
reInt1: 9984
reInt2: 9985
在0到10000(定点表示)之间的所有值中,大约47%可见该错误。 使用stagedDouble
转换时,它根本不会出现。 而且我不明白为什么两个整数不同。 我正在使用GCC 6.3.0。
编辑:改进了代码段,以显示变量
percent
并统一两个语句之间的系数。 将100
更改为double
似乎是质量更改,可能会影响输出,但不会更改程序中的任何内容。
percent
浮动吗? 如果是这样,请查看您要乘的类型。
reInt1
是double * float
而stagedDouble
是int * float
。 混淆浮点数学运算会导致这些类型的舍入错误。
将100都更改为double
或int
得到相同的答案。
报告的行为与声明为float
percent
一致,并且对float
和double
使用IEEE 754基本的32位和64位二进制浮点。
uint16_t reInt1 = (uint16_t)(100.0 * percent);
由于100.0
是一个double
恒定,此转换percent
至double
,在执行乘法double
,并将结果转换uint16_t
。 乘法可能具有非常小的舍入误差,最大为双精度格式的½ULP,相对误差约为2 -53 。
double stagedDouble = 100 * percent;
uint16_t reInt2 = (uint16_t)stagedDouble;
由于100
是一个int
常量,因此将100
转换为float
,在float
执行乘法,然后将结果转换为uint16_t
。 乘法中的舍入误差可能高达float
格式的1/2 ULP,相对误差约为2 -24 。
由于所有值都接近整数的百分之一,因此上/下的错误比率为50:50,结果将接近整数阈值所需结果的一半。 在乘法中,所有值分别为0、25、50或100的百分之一将是精确的(因为25/100是¼,这在二进制浮点数中可精确表示),因此96/100将具有舍入错误。 如果float
和double
舍入误差的方向表现为独立的,统一的随机变量,则大约一半会在不同方向上舍入,从而产生不同的结果,从而导致约48%的不匹配,这与问题中报告的47%一致。
(但是,当我测量实际结果时,我发现float
和double
方法之间有42%的差异。我怀疑这与舍入前float
乘法中的尾随位有关—分布可能不像两种可能性。可能是OP的代码以某种方式准备percent
值,而不是将整数值除以100。)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.