dnan的好处2
恕我直言,这是更好的方法,甚至不是异常,而是错误.
只要你有NaN值,程序就是无用和损坏的.
只要变量变为NaN,就应该抛它,而不仅是在操作时.
如下成立时它应该抛:
1,初化值为NaN并在操作中使用
2,在初化外,置值为NaN
3,从函数返回NaN
这解决所有问题,因为:
你永远不会在整个程序中传播NaN值,从而更易追踪它.
NaN严格告诉你,在使用某些值前,是错误的
无法有效地解决它,即你必须实际修复所有NaN.
D允许使用std.math.hardware.FloatingPointControl接口启用浮点异常.
当然,这些是硬件异常,因此你获得SIGFPE而不是带回溯的抛错误.要转换为D错误,需要安装信号处理器,就像etc.linux.memoryerror那样(或Windows上的等价物)
可惜,似乎在实践中不管用,因为仅在创建NaN时,而不是在传播时触发"无效"浮点异常.由于是在编译时而不是运行时创建默认初化float/double变量的初始NaN值,因此不会触发异常.
示例:
import std.math.hardware;
float div(float x, float y)
{
return x / y;
}
void main()
{
FloatingPointControl fpctrl;
fpctrl.enableExceptions(FloatingPointControl.severeExceptions);
float f = float.nan; // ok -无异常
float g = div(0.0f, 0.0f); // SIGFPE崩溃.
}
NaN总是错误的.0.0无处不在.如果从不查看输出,则都不会注意到其中错误.
你需要分析数据流.我没有把DFA放在前端,因为这会使它变慢而无好处.
不必害怕在输出中获得NaN.相反应该很高兴,因为你*知道*有错误.
D是帮助程序员创建正确,健壮和无错误程序的工具.
检查整
你正在推广使用结构初值作为特征,而不是基本类型的初值.
不同在它必须为有意设置的字段值,并且仅针对该字段值.它不是默认值.
double compute_pop ()
{
double pop;
// 忽略实现,偶遇nan
pop = sqrt (-1.);
pop += 0.;
return pop;
}
int main ()
{
auto pop = compute_pop ();
if (pop < .5)
writeln ("不下雨");
else
writeln ("会下雨");
return 0;
}
前几天我玩isNaN.用它来检查访问函数中的初化,来缓存昂贵计算.在注意到发布版本出现故障前,这非常有效.过了一段时间,我才发现ldc为发布版本,提供了fastmath选项,它假定没有NaN,而这导致isNaN错误工作.
示例:
import std;
void main()
{
assert(isNaN(double.nan));
}
用--ffast-math -O选项结合ldc编译.
isnan实现:
bool isNaN(X)(X x) @nogc @trusted pure nothrow
if (isFloatingPoint!(X))
{
version (all)
{
return x != x;
}
else
{
/*
历史背景保存的代码.至少在英特尔,简单的测试`x!=x`使用一条在一条指令中运行循环的`(ucomiss/ucomisd)`专用指令.`80`位和`128`位的代码更大,但仍小于下面基于整数的方法
*/
// 略...
}
}
这不仅与Walter的理解相悖,而且与IEEE754标准相悖.这是一篇更详细文章:小心快速数学
具有快速数学的LLVM假定所有浮点操作数都是有限的,即可在编译时优化x!=x为false.足够聪明的优化器,如果识别出NaN的位模式,原则上可优化检查位模式.
UDA等的好处之一是,可用它们来选择LLVM可执行的特定优化,而无需选择明显危险的优化.
1,2,3
不要滥用异常
0<=NaN.确实,有时0是相等的.但它*不可能*更好.
顺便,十六进制数据没有NaN值.但经常初化它为:0xDEADBEEF
这在野外不太可能.因此,当转储十六进制数据且有DEADBEEF时,表明有未初化数据.
我既用它在返回给调用者前,初化malloc的数据,并为其设置释放后数据.它在清理未初化的分配数据和释放后使用错误方面非常有效.使用0x00远没有它有效.
浙公网安备 33010602011771号