我正在使用固定的小数点数字(使用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
似乎是一种质量变化,可能会影响输出,但不会改变程序中的任何内容。
答案 0 :(得分:2)
percent
是浮点数吗?如果是这样,请查看您要乘法的类型。
reInt1
是double * float
,而stagedDouble
是int * 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将具有舍入错误。如果float
和uint16_t
舍入误差的方向表现为独立的,统一的随机变量,则大约一半会在不同方向上舍入,从而产生不同的结果,从而导致48%的不匹配,这与47在问题中报告的百分比。
(但是,当我测量实际结果时,我发现float
和float
方法之间有42%的差异。我怀疑这与{{1}中的尾随位有关}在四舍五入之前进行乘法运算-分布可能不像两种可能性的均匀分布。可能是OP的代码以某种方式准备了double
值,而不是将整数值除以100。)