Move floating-point math functions into a new lib/math

This commit is contained in:
Nick Mathewson 2018-06-28 12:24:45 -04:00
parent 6178a9f758
commit bdea94a665
19 changed files with 254 additions and 169 deletions

2
.gitignore vendored
View File

@ -187,6 +187,8 @@ uptime-*.json
/src/lib/libtor-log-testing.a
/src/lib/libtor-malloc.a
/src/lib/libtor-malloc-testing.a
/src/lib/libtor-math.a
/src/lib/libtor-math-testing.a
/src/lib/libtor-memarea.a
/src/lib/libtor-memarea-testing.a
/src/lib/libtor-net.a

View File

@ -48,6 +48,7 @@ TOR_UTIL_LIBS = \
src/lib/libtor-net.a \
src/lib/libtor-thread.a \
src/lib/libtor-memarea.a \
src/lib/libtor-math.a \
src/lib/libtor-log.a \
src/lib/libtor-lock.a \
src/lib/libtor-fdio.a \
@ -71,6 +72,7 @@ TOR_UTIL_TESTING_LIBS = \
src/lib/libtor-net-testing.a \
src/lib/libtor-thread-testing.a \
src/lib/libtor-memarea-testing.a \
src/lib/libtor-math-testing.a \
src/lib/libtor-log-testing.a \
src/lib/libtor-lock-testing.a \
src/lib/libtor-fdio-testing.a \

View File

@ -127,100 +127,6 @@ ENABLE_GCC_WARNING(aggregate-return)
* Math
* ===== */
/**
* 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) || ... */
}
/** 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;
}
/* =====
* String manipulation
* ===== */
@ -389,67 +295,3 @@ load_windows_system_library(const TCHAR *library_name)
return LoadLibrary(path);
}
#endif /* defined(_WIN32) */
/** 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
}

View File

@ -62,13 +62,6 @@ void tor_log_mallinfo(int severity);
#define bool_neq(a,b) (!(a)!=!(b))
/* Math functions */
double tor_mathlog(double d) ATTR_CONST;
long tor_lround(double d) ATTR_CONST;
int64_t tor_llround(double d) ATTR_CONST;
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);
int64_t clamp_double_to_int64(double number);
/* String manipulation */

View File

@ -14,6 +14,7 @@ include src/lib/include.libdonna.am
include src/lib/intmath/include.am
include src/lib/lock/include.am
include src/lib/log/include.am
include src/lib/math/include.am
include src/lib/memarea/include.am
include src/lib/malloc/include.am
include src/lib/net/include.am

View File

@ -0,0 +1,5 @@
orconfig.h
lib/cc/*.h
lib/log/*.h
lib/math/*.h

113
src/lib/math/fp.c Normal file
View File

@ -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
}

17
src/lib/math/fp.h Normal file
View File

@ -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

20
src/lib/math/include.am Normal file
View File

@ -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

67
src/lib/math/laplace.c Normal file
View File

@ -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;
}

16
src/lib/math/laplace.h Normal file
View File

@ -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

View File

@ -34,6 +34,8 @@
#include "or/entrynodes.h"
#include "or/networkstatus.h"
#include "or/relay.h"
#include "lib/math/fp.h"
#include "lib/math/laplace.h"
#include "or/cell_st.h"
#include "or/cpath_build_state_st.h"
@ -1574,4 +1576,3 @@ pathbias_scale_use_rates(entry_guard_t *guard)
entry_guards_changed();
}
}

View File

@ -40,6 +40,7 @@
#include "or/statefile.h"
#include "or/circuitlist.h"
#include "or/circuituse.h"
#include "lib/math/fp.h"
#include "or/crypt_path_st.h"
#include "or/origin_circuit_st.h"
@ -1946,4 +1947,3 @@ cbt_control_event_buildtimeout_set(const circuit_build_times_t *cbt,
tor_free(args);
}

View File

@ -56,6 +56,7 @@
#include "or/rephist.h"
#include "or/router.h"
#include "or/routerlist.h"
#include "lib/math/fp.h"
#include "or/cpath_build_state_st.h"
#include "or/dir_connection_st.h"
@ -3152,4 +3153,3 @@ circuit_read_valid_data(origin_circuit_t *circ, uint16_t relay_body_len)
tor_add_u32_nowrap(circ->n_overhead_read_circ_bw,
RELAY_PAYLOAD_SIZE-relay_body_len);
}

View File

@ -138,6 +138,7 @@
#include "or/routerset.h"
#include "or/transports.h"
#include "or/statefile.h"
#include "lib/math/fp.h"
#include "or/node_st.h"
#include "or/origin_circuit_st.h"

View File

@ -94,6 +94,8 @@
#include "lib/container/bloomfilt.h"
#include "lib/container/order.h"
#include "lib/math/fp.h"
#include "lib/math/laplace.h"
static void bw_arrays_init(void);
static void predicted_ports_alloc(void);

View File

@ -121,6 +121,7 @@
#include "or/routerset.h"
#include "lib/sandbox/sandbox.h"
#include "or/torcert.h"
#include "lib/math/fp.h"
#include "or/dirauth/dirvote.h"
#include "or/dirauth/mode.h"

View File

@ -13,6 +13,7 @@
#include "or/channeltls.h"
#define SCHEDULER_PRIVATE_
#include "or/scheduler.h"
#include "lib/math/fp.h"
#include "or/or_connection_st.h"
@ -835,4 +836,3 @@ scheduler_can_use_kist(void)
}
#endif /* defined(HAVE_KIST_SUPPORT) */

View File

@ -29,6 +29,8 @@
#include "lib/process/subprocess.h"
#include "lib/intmath/weakrng.h"
#include "lib/thread/numcpus.h"
#include "lib/math/fp.h"
#include "lib/math/laplace.h"
#ifdef HAVE_PWD_H
#include <pwd.h>