I need to deliberately return a 'NAN' from a LibraryLink function, which is expected to return mcomplex
so it appears as Indeterminate
in Mathematica.
MArgument_setComplex(Res, ???);
return LIBRARY_NO_ERROR;
What do I put in ???
?
It is not clear to me how well IEEE 754 NaNs or infinities are supported in Mathematica. I have seen values come out as an unusable expression (instead of Indeterminate
) when sending them through MathLink. I do not understand when and why this happens, but it's good to keep in mind that NaNs have multiple representations, and there's multiple kinds of them.
You can create a NaN using the nan()
function from the math.h
header, and insert it into an mcomplex
:
double dnan = nan("");
mcomplex cnan = {dnan, dnan};
With LTemplate, this is a bit simpler, as it uses std::complex<double>
as the complex type. Thus std::nan("")
can automatically be converted to the complex version.
Below is an LTemplate program that tests sending multiple kinds of NaNs in multiple ways (as a simple double
, as part of an array, with or without MathLink). I tested it in 10.0.2 and 11.2.0 on OS X, and the result was always Indeterminate
instead of an unusable value. Thus, at least on this platform, this seems to be a safe way to pass back an Indeterminate
. Perhaps someone can test on other platforms.
SetDirectory@CreateDirectory[];
Needs["LTemplate`"]
tem = LClass["NaNDemo",
{
LFun["NaN", {}, Complex],
LFun["quietNaN", {}, Real],
LFun["signalingNaN", {}, Real],
LFun["quietNaNML", LinkObject],
LFun["signalingNaNML", LinkObject],
LFun["arrayQuietNaN", {}, {Real, 1}],
LFun["arraySignalingNaN", {}, {Real, 1}],
LFun["arrayQuietNaNML", LinkObject],
LFun["arraySignalingNaNML", LinkObject]
}
];
code = "
#include <mlstream.h>
#include <cmath>
#include <limits>
struct NaNDemo {
mma::complex_t NaN() { return std::nan(\"\"); }
double quietNaN() { return std::numeric_limits<double>::quiet_NaN(); }
double signalingNaN() { return std::numeric_limits<double>::signaling_NaN(); }
void quietNaNML(MLINK link) {
mlStream ml(link);
ml >> mlCheckArgs(0);
ml.newPacket();
ml << quietNaN();
}
void signalingNaNML(MLINK link) {
mlStream ml(link);
ml >> mlCheckArgs(0);
ml.newPacket();
ml << signalingNaN();
}
mma::RealTensorRef arrayQuietNaN() {
auto vec = mma::makeVector<double>(1);
vec[0] = quietNaN();
return vec;
}
mma::RealTensorRef arraySignalingNaN() {
auto vec = mma::makeVector<double>(1);
vec[0] = signalingNaN();
return vec;
}
void arrayQuietNaNML(MLINK link) {
mlStream ml(link);
ml >> mlCheckArgs(0);
ml.newPacket();
ml << std::vector<double>({quietNaN()});
}
void arraySignalingNaNML(MLINK link) {
mlStream ml(link);
ml >> mlCheckArgs(0);
ml.newPacket();
ml << std::vector<double>({signalingNaN()});
}
};";
Export["NaNDemo.h", code, "String"]
CompileTemplate[tem,
"CompileOptions" -> {"-mmacosx-version-min=10.9"}]
LoadTemplate[tem]
obj = Make[NaNDemo];
obj@"NaN"[]
(* Indeterminate *)
obj@"quietNaN"[]
(* Indeterminate *)
obj@"signalingNaN"[]
(* Indeterminate *)
obj@"quietNaNML"[]
(* Indeterminate *)
obj@"signalingNaNML"[]
(* Indeterminate *)
obj@"arrayQuietNaN"[]
(* {Indeterminate} *)
obj@"arraySignalingNaN"[]
(* {Indeterminate} *)
obj@"arrayQuietNaNML"[]
(* {Indeterminate} *)
obj@"arraySignalingNaNML"[]
(* {Indeterminate} *)
0./0.
? $\endgroup$double myNaN = 0./0.
, but I don't know how to turn it into anmcomplex
. $\endgroup$mcomplex z = {0.,0.}
. Or maybemcomplex zNaN = {0./0., 0./0.}
? $\endgroup$mcomplex
like that. I wonder if that's the "official" way of making it returnIndeterminate
. $\endgroup$