C和C ++提供了几种宽度的浮点数据类型,但它们保留了未指定的精度。编译器可以自由地使用理想化算法来简化表达式,在计算float
值以上的表达式时使用双精度,或者使用双精度寄存器来保持float
变量或公共值的值子表达式。
纠正我,如果我错了错误,请参阅编辑,但将内存中的float
提升到双精度寄存器甚至是合法的,所以存储一个值然后加载它不一定会截断位。
将数字转换为较低精度的最安全,最便携的方法是什么?理想情况下,它也应该有效,在SSE2上编译为cvtsd2ss
。 (所以,虽然volatile
可能是一个答案,但我更喜欢更好的东西。)
编辑:总结一些评论和发现......
FP_CONTRACT on
的C中允许表达式简化。float
使用双精度是不允许(在C或C ++中)。然而,一些编译器(尤其是x86-32上的GCC)非法忘记了一些精确转换。
编辑2:有些人对于未能缩小中间结果的一致性表示怀疑。
C11§5.2.4.2.2/ 9(与答案中引用的C99 ref相同)具体关于“删除所有额外范围和精度”,因为它指定了如何以更宽的精度完成其他计算。在几个符合要求的替代准则中,“不确定”,对我来说意味着没有任何限制。
C11§7.12.2和§6.5/ 8定义#pragma STDC FP_CONTRACT on
,使编译器尽可能使用无限精度。
收缩表达式中的中间操作被评估为无限范围和精度,而最终操作被舍入为由表达式评估方法确定的格式。合同表达式也可能省略浮点异常的引发。
C ++ 14同样特别放弃了有限精度和中间结果范围的约束。 N4567§5/ 12:
浮动操作数的值和浮动表达式的结果可以表示为比该类型所需的精度和范围更大的精度和范围;因此,类型不会改变。
请注意,允许标识x - x = 0
将a + b - b + c
简化为a + c
与添加可转换或关联不同。 a + b + c
仍然与a + c + b
或a + (b + c)
不同,当CPU仅提供两个加数和舍入结果时。
答案 0 :(得分:4)
C99 5.2.4.2.2p8明确地说
赋值和强制转换[..]删除所有额外的范围和精度
因此,如果您想将范围和精度限制为浮点数,只需转换为float
,或指定给float
变量。
您甚至可以执行(double)((float)d)
之类的操作(使用额外的括号以确保人类正确读取它),将变量d
限制为float
精度和范围,然后将其强制转换为double
。 (即使d
是double
,标准C编译器也不允许对其进行优化;它必须将精度和范围限制为float
的精度和范围。)
我已经在例如实际实施中使用了它。 Kahan summation algorithm,可用于允许C编译器进行非常积极的优化,但没有失效的风险。
答案 1 :(得分:1)
我不太确定我在这里分享你的恐惧......我尝试了这个荣耀的演员作为一个功能:
float to_float(double x)
{
return (float) x;
}
当输入Compiler Explorer时,我明白了:
to_float(double):
push rbp
mov rbp, rsp
movsd QWORD PTR [rbp-8], xmm0
cvtsd2ss xmm0, QWORD PTR [rbp-8]
pop rbp
ret
这似乎立即生成了请求的操作码(cvtsd2ss
),我甚至没有输入任何编译器选项来强制SSE2或任何东西。
我会说演员必须转换为目标类型,据我所知,编译器不能随意忽略演员表。
您是否可以提供一些您认为编译器可以忽略演员表的情况?也许在代码中隐藏着某种未定义的行为,这使得编译器采用了意想不到的快捷方式。