diff options
author | Nick Mathewson <nickm@torproject.org> | 2018-06-28 12:24:45 -0400 |
---|---|---|
committer | Nick Mathewson <nickm@torproject.org> | 2018-06-28 12:24:45 -0400 |
commit | bdea94a6653240e0e477eda186ceccbe6be00535 (patch) | |
tree | 64e57e8684f65fba4b0e953ef2a99d7fdb353a06 /src/lib/math | |
parent | 6178a9f758d60f5fc896644f1b9b0aaf4c32f2a5 (diff) | |
download | tor-bdea94a6653240e0e477eda186ceccbe6be00535.tar.gz tor-bdea94a6653240e0e477eda186ceccbe6be00535.zip |
Move floating-point math functions into a new lib/math
Diffstat (limited to 'src/lib/math')
-rw-r--r-- | src/lib/math/.may_include | 5 | ||||
-rw-r--r-- | src/lib/math/fp.c | 113 | ||||
-rw-r--r-- | src/lib/math/fp.h | 17 | ||||
-rw-r--r-- | src/lib/math/include.am | 20 | ||||
-rw-r--r-- | src/lib/math/laplace.c | 67 | ||||
-rw-r--r-- | src/lib/math/laplace.h | 16 |
6 files changed, 238 insertions, 0 deletions
diff --git a/src/lib/math/.may_include b/src/lib/math/.may_include new file mode 100644 index 0000000000..1fd26864dc --- /dev/null +++ b/src/lib/math/.may_include @@ -0,0 +1,5 @@ +orconfig.h + +lib/cc/*.h +lib/log/*.h +lib/math/*.h diff --git a/src/lib/math/fp.c b/src/lib/math/fp.c new file mode 100644 index 0000000000..d1c4428251 --- /dev/null +++ b/src/lib/math/fp.c @@ -0,0 +1,113 @@ +/* Copyright (c) 2003, Roger Dingledine + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "orconfig.h" +#include "lib/math/fp.h" + +#include <math.h> + +/** + * Returns the natural logarithm of d base e. We defined this wrapper here so + * to avoid conflicts with old versions of tor_log(), which were named log(). + */ +double +tor_mathlog(double d) +{ + return log(d); +} + +/** Return the long integer closest to <b>d</b>. We define this wrapper + * here so that not all users of math.h need to use the right incantations + * to get the c99 functions. */ +long +tor_lround(double d) +{ +#if defined(HAVE_LROUND) + return lround(d); +#elif defined(HAVE_RINT) + return (long)rint(d); +#else + return (long)(d > 0 ? d + 0.5 : ceil(d - 0.5)); +#endif /* defined(HAVE_LROUND) || ... */ +} + +/** Return the 64-bit integer closest to d. We define this wrapper here so + * that not all users of math.h need to use the right incantations to get the + * c99 functions. */ +int64_t +tor_llround(double d) +{ +#if defined(HAVE_LLROUND) + return (int64_t)llround(d); +#elif defined(HAVE_RINT) + return (int64_t)rint(d); +#else + return (int64_t)(d > 0 ? d + 0.5 : ceil(d - 0.5)); +#endif /* defined(HAVE_LLROUND) || ... */ +} + +/** Cast a given double value to a int64_t. Return 0 if number is NaN. + * Returns either INT64_MIN or INT64_MAX if number is outside of the int64_t + * range. */ +int64_t +clamp_double_to_int64(double number) +{ + int exponent; + +#if defined(MINGW_ANY) && GCC_VERSION >= 409 +/* + Mingw's math.h uses gcc's __builtin_choose_expr() facility to declare + isnan, isfinite, and signbit. But as implemented in at least some + versions of gcc, __builtin_choose_expr() can generate type warnings + even from branches that are not taken. So, suppress those warnings. +*/ +#define PROBLEMATIC_FLOAT_CONVERSION_WARNING +DISABLE_GCC_WARNING(float-conversion) +#endif /* defined(MINGW_ANY) && GCC_VERSION >= 409 */ + +/* + With clang 4.0 we apparently run into "double promotion" warnings here, + since clang thinks we're promoting a double to a long double. + */ +#if defined(__clang__) +#if __has_warning("-Wdouble-promotion") +#define PROBLEMATIC_DOUBLE_PROMOTION_WARNING +DISABLE_GCC_WARNING(double-promotion) +#endif +#endif /* defined(__clang__) */ + + /* NaN is a special case that can't be used with the logic below. */ + if (isnan(number)) { + return 0; + } + + /* Time to validate if result can overflows a int64_t value. Fun with + * float! Find that exponent exp such that + * number == x * 2^exp + * for some x with abs(x) in [0.5, 1.0). Note that this implies that the + * magnitude of number is strictly less than 2^exp. + * + * If number is infinite, the call to frexp is legal but the contents of + * are exponent unspecified. */ + frexp(number, &exponent); + + /* If the magnitude of number is strictly less than 2^63, the truncated + * version of number is guaranteed to be representable. The only + * representable integer for which this is not the case is INT64_MIN, but + * it is covered by the logic below. */ + if (isfinite(number) && exponent <= 63) { + return (int64_t)number; + } + + /* Handle infinities and finite numbers with magnitude >= 2^63. */ + return signbit(number) ? INT64_MIN : INT64_MAX; + +#ifdef PROBLEMATIC_DOUBLE_PROMOTION_WARNING +ENABLE_GCC_WARNING(double-promotion) +#endif +#ifdef PROBLEMATIC_FLOAT_CONVERSION_WARNING +ENABLE_GCC_WARNING(float-conversion) +#endif +} diff --git a/src/lib/math/fp.h b/src/lib/math/fp.h new file mode 100644 index 0000000000..b35c18a1c7 --- /dev/null +++ b/src/lib/math/fp.h @@ -0,0 +1,17 @@ +/* Copyright (c) 2003, Roger Dingledine + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#ifndef TOR_FP_H +#define TOR_FP_H + +#include "lib/cc/compat_compiler.h" +#include "lib/cc/torint.h" + +double tor_mathlog(double d) ATTR_CONST; +long tor_lround(double d) ATTR_CONST; +int64_t tor_llround(double d) ATTR_CONST; +int64_t clamp_double_to_int64(double number); + +#endif diff --git a/src/lib/math/include.am b/src/lib/math/include.am new file mode 100644 index 0000000000..b088b3f3cc --- /dev/null +++ b/src/lib/math/include.am @@ -0,0 +1,20 @@ + +noinst_LIBRARIES += src/lib/libtor-math.a + +if UNITTESTS_ENABLED +noinst_LIBRARIES += src/lib/libtor-math-testing.a +endif + +src_lib_libtor_math_a_SOURCES = \ + src/lib/math/fp.c \ + src/lib/math/laplace.c + + +src_lib_libtor_math_testing_a_SOURCES = \ + $(src_lib_libtor_math_a_SOURCES) +src_lib_libtor_math_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS) +src_lib_libtor_math_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) + +noinst_HEADERS += \ + src/lib/math/fp.h \ + src/lib/math/laplace.h diff --git a/src/lib/math/laplace.c b/src/lib/math/laplace.c new file mode 100644 index 0000000000..8e45a1fb33 --- /dev/null +++ b/src/lib/math/laplace.c @@ -0,0 +1,67 @@ +/* Copyright (c) 2003, Roger Dingledine + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "orconfig.h" +#include "lib/math/laplace.h" +#include "lib/math/fp.h" + +#include "lib/log/util_bug.h" + +#include <math.h> +#include <stdlib.h> + +/** Transform a random value <b>p</b> from the uniform distribution in + * [0.0, 1.0[ into a Laplace distributed value with location parameter + * <b>mu</b> and scale parameter <b>b</b>. Truncate the final result + * to be an integer in [INT64_MIN, INT64_MAX]. */ +int64_t +sample_laplace_distribution(double mu, double b, double p) +{ + double result; + tor_assert(p >= 0.0 && p < 1.0); + + /* This is the "inverse cumulative distribution function" from: + * http://en.wikipedia.org/wiki/Laplace_distribution */ + if (p <= 0.0) { + /* Avoid taking log(0.0) == -INFINITY, as some processors or compiler + * options can cause the program to trap. */ + return INT64_MIN; + } + + result = mu - b * (p > 0.5 ? 1.0 : -1.0) + * tor_mathlog(1.0 - 2.0 * fabs(p - 0.5)); + + return clamp_double_to_int64(result); +} + +/** Add random noise between INT64_MIN and INT64_MAX coming from a Laplace + * distribution with mu = 0 and b = <b>delta_f</b>/<b>epsilon</b> to + * <b>signal</b> based on the provided <b>random</b> value in [0.0, 1.0[. + * The epsilon value must be between ]0.0, 1.0]. delta_f must be greater + * than 0. */ +int64_t +add_laplace_noise(int64_t signal_, double random_, double delta_f, + double epsilon) +{ + int64_t noise; + + /* epsilon MUST be between ]0.0, 1.0] */ + tor_assert(epsilon > 0.0 && epsilon <= 1.0); + /* delta_f MUST be greater than 0. */ + tor_assert(delta_f > 0.0); + + /* Just add noise, no further signal */ + noise = sample_laplace_distribution(0.0, + delta_f / epsilon, + random_); + + /* Clip (signal + noise) to [INT64_MIN, INT64_MAX] */ + if (noise > 0 && INT64_MAX - noise < signal_) + return INT64_MAX; + else if (noise < 0 && INT64_MIN - noise > signal_) + return INT64_MIN; + else + return signal_ + noise; +} diff --git a/src/lib/math/laplace.h b/src/lib/math/laplace.h new file mode 100644 index 0000000000..b22862e64a --- /dev/null +++ b/src/lib/math/laplace.h @@ -0,0 +1,16 @@ +/* Copyright (c) 2003, Roger Dingledine + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#ifndef TOR_LAPLACE_H +#define TOR_LAPLACE_H + +#include "lib/cc/compat_compiler.h" +#include "lib/cc/torint.h" + +int64_t sample_laplace_distribution(double mu, double b, double p); +int64_t add_laplace_noise(int64_t signal, double random, double delta_f, + double epsilon); + +#endif |