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 arithmetic operations (
+,-,*,/) 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.
Single precision
| Original function | Uncertain function |
|---|---|
float acosf(float) | uncertain float uacosf(uncertain float) |
float acoshf(float) | uncertain float uacoshf(uncertain float) |
float asinf(float) | uncertain float uasinf(uncertain float) |
float asinhf(float) | uncertain float uasinhf(uncertain float) |
float atanf(float) | uncertain float uatanf(uncertain float) |
float atanhf(float) | uncertain float uatanhf(uncertain float) |
float cbrtf(float) | uncertain float ucbrtf(uncertain float) |
float ceilf(float) | uncertain float uceilf(uncertain float) |
float cosf(float) | uncertain float ucosf(uncertain float) |
float coshf(float) | uncertain float ucoshf(uncertain float) |
float erff(float) | uncertain float uerff(uncertain float) |
float erfcf(float) | uncertain float uerfcf(uncertain float) |
float expf(float) | uncertain float uexpf(uncertain float) |
float exp2f(float) | uncertain float uexp2f(uncertain float) |
float expm1f(float) | uncertain float uexpm1f(uncertain float) |
float fabsf(float) | uncertain float ufabsf(uncertain float) |
float floorf(float) | uncertain float ufloorf(uncertain float) |
float lgammaf(float) | uncertain float ulgammaf(uncertain float) |
float logf(float) | uncertain float ulogf(uncertain float) |
float log10f(float) | uncertain float ulog10f(uncertain float) |
float log1pf(float) | uncertain float ulog1pf(uncertain float) |
float log2f(float) | uncertain float ulog2f(uncertain float) |
float logbf(float) | uncertain float ulogbf(uncertain float) |
float nearbyintf(float) | uncertain float unearbyintf(uncertain float) |
float rintf(float) | uncertain float urintf(uncertain float) |
float roundf(float) | uncertain float uroundf(uncertain float) |
float sinf(float) | uncertain float usinf(uncertain float) |
float sinhf(float) | uncertain float usinhf(uncertain float) |
float sqrtf(float) | uncertain float usqrtf(uncertain float) |
float tanf(float) | uncertain float utanf(uncertain float) |
float tanhf(float) | uncertain float utanhf(uncertain float) |
float tgammaf(float) | uncertain float utgammaf(uncertain float) |
float truncf(float) | uncertain float utruncf(uncertain float) |
float atan2f(float, float) | uncertain float uatan2f(uncertain float, uncertain float) |
float fmaxf(float, float) | uncertain float ufmaxf(uncertain float, uncertain float) |
float fminf(float, float) | uncertain float ufminf(uncertain float, uncertain float) |
float powf(float, float) | uncertain float upowf(uncertain float, uncertain float) |
Double precision
| Original function | Uncertain function |
|---|---|
double acos(double) | uncertain double uacos(uncertain double) |
double acosh(double) | uncertain double uacosh(uncertain double) |
double asin(double) | uncertain double uasin(uncertain double) |
double asinh(double) | uncertain double uasinh(uncertain double) |
double atan(double) | uncertain double uatan(uncertain double) |
double atanh(double) | uncertain double uatanh(uncertain double) |
double cbrt(double) | uncertain double ucbrt(uncertain double) |
double ceil(double) | uncertain double uceil(uncertain double) |
double cos(double) | uncertain double ucos(uncertain double) |
double cosh(double) | uncertain double ucosh(uncertain double) |
double erf(double) | uncertain double uerf(uncertain double) |
double erfc(double) | uncertain double uerfc(uncertain double) |
double exp(double) | uncertain double uexp(uncertain double) |
double exp2(double) | uncertain double uexp2(uncertain double) |
double expm1(double) | uncertain double uexpm1(uncertain double) |
double fabs(double) | uncertain double ufabs(uncertain double) |
double floor(double) | uncertain double ufloor(uncertain double) |
double lgamma(double) | uncertain double ulgamma(uncertain double) |
double log(double) | uncertain double ulog(uncertain double) |
double log10(double) | uncertain double ulog10(uncertain double) |
double log1p(double) | uncertain double ulog1p(uncertain double) |
double log2(double) | uncertain double ulog2(uncertain double) |
double logb(double) | uncertain double ulogb(uncertain double) |
double nearbyint(double) | uncertain double unearbyint(uncertain double) |
double rint(double) | uncertain double urint(uncertain double) |
double round(double) | uncertain double uround(uncertain double) |
double sin(double) | uncertain double usin(uncertain double) |
double sinh(double) | uncertain double usinh(uncertain double) |
double sqrt(double) | uncertain double usqrt(uncertain double) |
double tan(double) | uncertain double utan(uncertain double) |
double tanh(double) | uncertain double utanh(uncertain double) |
double tgamma(double) | uncertain double utgamma(uncertain double) |
double trunc(double) | uncertain double utrunc(uncertain double) |
double atan2(double, double) | uncertain double uatan2(uncertain double, uncertain double) |
double fmax(double, double) | uncertain double ufmax(uncertain double, uncertain double) |
double fmin(double, double) | uncertain double ufmin(uncertain double, uncertain double) |
double pow(double, double) | uncertain double upow(uncertain double, uncertain double) |