Uncertain Type Modifier
Signaloid UxHw® technology provides the option to explicitly declare which variables and operations are operating on
distributional data, in the form of the Uncertain Type Modifier (UTM). The UTM is a type modifier (attribute) for the C/C++ language.
You can declare program variables or function parameters as uncertain float or uncertain double so that the declaration instructs
the Signaloid UxHw SDK compiler infrastructure to generate distribution-extended code only for the variables and function parameters declared as uncertain.
Using this feature customers have precise control over which parts of their programs operate on distributions.
In order to use the UTM, you need to set the compilation flag ENABLE_UNCERTAIN_TYPE_MODIFIER=ON in the
config.mk.
For compatibility purposes, the uncertain keyword is defined in the uxhw.h file and all source files that
use the uncertain type need to include uxhw.h. Applications using the uncertain keyword can also be compiled with
ENABLE_UNCERTAIN_TYPE_MODIFIER=OFF but in that case all uncertain type declarations have no impact on the
compilation process or the generated code.
#include <uxhw.h>
/*
* Declaring variables as uncertain.
*/
uncertain float a;
uncertain double b;
/*
* Declaring a function that returns an uncertain value.
* Function parameters can also be uncertain.
*/
uncertain float
foo(uncertain double arg);
Main advantages
- UTM provides explicit control over which parts of an applications need to perform distributional operations and which not. For applications that rely on distributional arithmetic only for small fractions of the application, the UTM can remove the run-time overhead of executing unnecessary distributional operations.
- UTM leads to smaller binaries compared to the default Signaloid UxHw binaries, since only the necessary parts of the application are transformed.
- UTM makes it easier for programmers to reason about the distributional execution of their programs, by providing a set of
compile-time warnings. Warnings for implicit typecasts and type mismatches between
uncertainand non-uncertainvalues provide useful diagnostics to programmers and can guide them towards a solution that utilises Signaloid's distribution-extended capabilities, without any unnecessary overhead.
Operation semantics
In this section, we will define the operation semantics for variables declared as uncertain.
- All binary operations (
add,sub,mul,div) betweenuncertainvariables yield anuncertainresult. - All binary operations between an
uncertainvariable and a normal floating-point or integer value also yield anuncertainresult. Promotion rules are similar to normal C semantics meaning that anuncertain floatand adoubleoperation will yield anuncertain doubleresult. - Casting an integer or a floating-point to an
uncertainfloating-point is permitted. - Casting an
uncertainfloating-point to a floating-point is also permitted but that discards the distributional information.
#include <uxhw.h>
/*
* Performs distributional floating-point addition.
*/
uncertain double
add(uncertain double a, uncertain double b)
{
return a + b;
}
/*
* Performs distributional floating-point subtraction.
*/
uncertain float
sub(uncertain float a, uncertain float b)
{
return a - b;
}
/*
* Will not perform any distributional operation.
*/
float
mul(float a, float b)
{
return a * b;
}
/*
* Will perform a distributional division.
* Will discard the distributional value of the result
* as it returns double. This implicit cast from
* uncertain double to double will generate a warning.
*/
double
div(uncertain double a, double b)
{
return a / b;
}
/*
* Will perform a distributional fused-multiply-add.
* Will discard the distributional value of the result
* as it returns double.
* This will not generate a warning as it is an explicit cast.
*/
double
fusedMultAdd(uncertain double a, uncertain double b, uncertain double c)
{
return (double) (a * b + c);
}
Printing, scanning, conversion from string
Similar to libm functions, we provide the uncertain counterpart of the strtod/strtof functions that convert a string
to an uncertain floating-point value.
uncertain float
strtouf(const char * str, char ** endptr);
uncertain double
strtoud(const char * str, char ** endptr);
When the UTM is enabled, printf family of functions do not print by default the Ux-string of the source uncertain floating-point
values. Instead, we provide the %U length modifier for the printf family of functions, or a macro called SignaloidUncertainModifier
that prints the Ux-string for uncertain floating-point values. If a non-uncertain floating-point value is given as an argument
to the printf a warning is generated, but a Ux-string will be generated. Also, for uncertain floating-point values, if we do not
use the %U length modifier, the printf family of functions will not print any Ux-string.
#include <stdio.h>
#include <uxhw.h>
int
main(void)
{
uncertain double value = UxHwDoubleUniformDist(0.1, 1.0);
/*
* Prints Ux-string.
*/
fprintf(stdout, "value = %Ulf\n", value);
/*
* Does not print Ux-string. Generates a compile-time warning.
*/
fprintf(stdout, "value = %lf\n", value);
/*
* The `SignaloidUncertainModifier` macro has
* the same effect.
*/
fprintf(stdout, "value = %" SignaloidUncertainModifier "lf\n", value);
return 0;
}
For the scanf family of functions, the same length modifier is used to declare that the argument pointer that scanf
will write to is an uncertain floating-point variable. The scanf implementation will still be able to parse Ux-strings
even if %U is not provided, however it will not attempt to write the distributional information to the normal floating-point
pointer argument.
#include <stdio.h>
#include <uxhw.h>
int
main(void)
{
uncertain double value;
/*
* Parses the Ux-string and stores it in `value`.
*/
sscanf(
"90.600000Ux04000000000000000A4056A666666666670000000A40568000000000000CCCCCCCCCCCCCCD405E0000000000000CCCCCCCCCCCCCCD40580000000000000CCCCCCCCCCCCCCD40340000000000000CCCCCCCCCCCCCCD4058C000000000000CCCCCCCCCCCCCCD40598000000000000CCCCCCCCCCCCCCD405C0000000000000CCCCCCCCCCCCCCD40510000000000000CCCCCCCCCCCCCCD40568000000000000CCCCCCCCCCCCCCC405B4000000000000CCCCCCCCCCCCCCC",
"%Ulf",
&value);
printf("value = %Ulf\n", value);
/*
* Will parse the Ux-string but will store only the floating-point part.
* Will also generate a warning.
*/
sscanf(
"90.600000Ux04000000000000000A4056A666666666670000000A40568000000000000CCCCCCCCCCCCCCD405E0000000000000CCCCCCCCCCCCCCD40580000000000000CCCCCCCCCCCCCCD40340000000000000CCCCCCCCCCCCCCD4058C000000000000CCCCCCCCCCCCCCD40598000000000000CCCCCCCCCCCCCCD405C0000000000000CCCCCCCCCCCCCCD40510000000000000CCCCCCCCCCCCCCD40568000000000000CCCCCCCCCCCCCCC405B4000000000000CCCCCCCCCCCCCCC",
"%lf",
&value);
/*
* Printing here will have a different Ux-string corresponding only to the particle
* value stored in `value`.
*/
printf("value = %Ulf\n", value);
return 0;
}
Using libm functions
With UTM we also provide a set of uncertain libm functions, because normal libm functions will no longer propagate the distributional
information, since their type is not uncertain. We provide the following libm functions that propagate the distributional
information. All uncertain libm functions are implemented as macros and therefore if ENABLE_UNCERTAIN_TYPE_MODIFIER=OFF they
all evaluate to the corresponding non-uncertain counterpart and will continue to propagate the distributional information.
uncertain double
uabs(uncertain double);
uncertain float
uabsf(uncertain float);
uncertain double
uacos(uncertain double);
uncertain float
uacosf(uncertain float);
uncertain double
uacosh(uncertain double);
uncertain float
uacoshf(uncertain float);
uncertain float
uasinf(uncertain float);
uncertain double
uasin(uncertain double);
uncertain float
uasinhf(uncertain float);
uncertain double
uasinh(uncertain double);
uncertain float
uatanf(uncertain float);
uncertain double
uatan(uncertain double);
uncertain float
uatanhf(uncertain float);
uncertain double
uatanh(uncertain double);
uncertain float
ucbrtf(uncertain float);
uncertain double
ucbrt(uncertain double);
uncertain float
uceilf(uncertain float);
uncertain double
uceil(uncertain double);
uncertain float
ucosf(uncertain float);
uncertain double
ucos(uncertain double);
uncertain float
ucoshf(uncertain float);
uncertain double
ucosh(uncertain double);
uncertain float
uerff(uncertain float);
uncertain double
uerf(uncertain double);
uncertain float
uerfcf(uncertain float);
uncertain double
uerfc(uncertain double);
uncertain float
uexpf(uncertain float);
uncertain double
uexp(uncertain double);
uncertain float
uexp2f(uncertain float);
uncertain double
uexp2(uncertain double);
uncertain float
uexpm1f(uncertain float);
uncertain double
uexpm1(uncertain double);
uncertain float
ufabsf(uncertain float);
uncertain double
ufabs(uncertain double);
uncertain float
ufloorf(uncertain float);
uncertain double
ufloor(uncertain double);
uncertain float
ugammaf(uncertain float);
uncertain double
ugamma(uncertain double);
uncertain float
ulgammaf(uncertain float);
uncertain double
ulgamma(uncertain double);
uncertain float
ulogf(uncertain float);
uncertain double
ulog(uncertain double);
uncertain float
ulog10f(uncertain float);
uncertain double
ulog10(uncertain double);
uncertain float
ulog1pf(uncertain float);
uncertain double
ulog1p(uncertain double);
uncertain float
ulog2f(uncertain float);
uncertain double
ulog2(uncertain double);
uncertain float
ulogbf(uncertain float);
uncertain double
ulogb(uncertain double);
uncertain float
unearbyintf(uncertain float);
uncertain double
unearbyint(uncertain double);
uncertain float
urintf(uncertain float);
uncertain double
urint(uncertain double);
uncertain float
uroundf(uncertain float);
uncertain double
uround(uncertain double);
uncertain float
usinf(uncertain float);
uncertain double
usin(uncertain double);
uncertain float
usinhf(uncertain float);
uncertain double
usinh(uncertain double);
uncertain float
usqrtf(uncertain float);
uncertain double
usqrt(uncertain double);
uncertain float
utanf(uncertain float);
uncertain double
utan(uncertain double);
uncertain float
utanhf(uncertain float);
uncertain double
utanh(uncertain double);
uncertain float
utgammaf(uncertain float);
uncertain double
utgamma(uncertain double);
uncertain float
utruncf(uncertain float);
uncertain double
utrunc(uncertain double);
uncertain float
uatan2f(uncertain float, uncertain float);
uncertain double
uatan2(uncertain double, uncertain double);
uncertain float
ufmaxf(uncertain float, uncertain float);
uncertain double
ufmax(uncertain double, uncertain double);
uncertain float
ufminf(uncertain float, uncertain float);
uncertain double
ufmin(uncertain double, uncertain double);
uncertain float
upowf(uncertain float, uncertain float);
uncertain double
upow(uncertain double, uncertain double);