blob: 5a6d18555450daeb80accd544c1517f0d4fe1c24 [file] [log] [blame]
//
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package com.google.privacy.differentialprivacy;
import static com.google.common.base.Preconditions.checkArgument;
/** Mathematical utilities for generating secure DP noise. */
final class SecureNoiseMath {
private SecureNoiseMath() {}
/**
* Returns the smallest power of 2 larger or equal to {@code x}. The value of {@code x} must be a
* finite positive number not greater than 2^1023. The result of this method is guaranteed to be
* an exact power of 2.
*/
public static double ceilPowerOfTwo(double x) {
checkArgument(x > 0.0, "Input must be positive. Provided value: %s", x);
checkArgument(Double.isFinite(x), "Input must be finite. Provided value: %s", x);
checkArgument(!Double.isNaN(x), "Input must be a number. Provided value: NaN");
// The following bit masks are based on the bit layout of double values in Java, which according
// to the IEEE standard is defined as "1*s 11*e 52*m" where "s" is the sign bit, "e" are the
// exponent bits and "m" are the mantissa bits.
final long exponentMask = 0x7ff0000000000000L;
final long mantissaMask = 0x000fffffffffffffL;
long bits = Double.doubleToLongBits(x);
long mantissaBits = bits & mantissaMask;
// Since x is a finite positive number, x is a power of 2 if and only if it has a mantissa of 0.
if (mantissaBits == 0x0000000000000000L) {
return x;
}
long exponentBits = bits & exponentMask;
long maxExponentBits = Double.doubleToLongBits(Double.MAX_VALUE) & exponentMask;
checkArgument(
exponentBits < maxExponentBits,
"Input must not be greater than 2^1023. Provided value: %s",
x);
// Increase the exponent bits by 1 to get the next power of 2. Note that this requires to add
// 0x0010000000000000L to the exponent bits in order to skip the mantissa.
return Double.longBitsToDouble(exponentBits + 0x0010000000000000L);
}
/**
* Rounds {@code x} to the closest multiple of {@code granularity}, where {@code granularity} is a
* power of 2. Because {@code granularity} must be a power of 2, the result is exact.
*/
public static double roundToMultipleOfPowerOfTwo(double x, double granularity) {
// granularity is a power of 2 if and only if it's a positive finite value with a mantissa of 0.
checkArgument(
granularity > 0.0
&& Double.isFinite(granularity)
&& (Double.doubleToLongBits(granularity) & 0x000fffffffffffffL) == 0L,
"Granularity must be a power of 2. Provided value: %s",
granularity);
if (Math.abs(x / granularity) < 1L << 54) {
// The absolute value of x / granularity is less than 2^54 and therefore it is in particular
// less than 2^63 and can be rounded to a long value without risking an overflow.
return Math.round(x / granularity) * granularity;
} else {
// The absolute value of x / granularity is greater or equal to 2^54 and therefore it is in
// particular greater than 2^53, which in turn implies that x / granularity contains no
// fractional bits. As a result no rounding is necessary to make x a multiple of the
// granularity.
return x;
}
}
/** Rounds {@code x} to the closest multiple of {@code granularity}. This operation is exact. */
public static long roundToMultiple(long x, long granularity) {
checkArgument(granularity > 0, "Granularity must be positive. Provided value: %s", granularity);
return ((x / granularity) + Math.round((x % granularity) / ((double) granularity)))
* granularity;
}
/**
* Computes the smallest double value that is larger than or equal to the provided long value.
*
* <p>Mapping from long to double for large long values (> 2^53) is inaccurate since they may not
* be represented as a double. The default conversion from long to double either rounds up or down
* the long value to the nearest representable double. This function ensures that {@code n} <=
* (double) {@code nextLargerDouble(long n)}.
*/
public static double nextLargerDouble(long n) {
// Large long values n may lie between two representable double values a and b,
// i.e., a < n < b, (note that in this case a and b are guaranteed to be integers).
// If the standard conversion to double rounds the long value down,
// e.g. (double) n = a, the difference a - n will be negative, indicating that the result needs
// to be incremented to the next double value b.
double result = n;
long dif = (long) result - n;
return dif >= 0 ? result : Math.nextUp(result);
}
/**
* See {@link #nextLargerDouble(long)}.
*
* <p>As opposed to the latter method, this computes the largest double value that is smaller than
* or equal to the provided long value {@code n} >= {@code nextSmallerDouble(long n)}.
*/
public static double nextSmallerDouble(long n) {
// Large long values n may lie between two representable double values a and b,
// i.e., a < n < b, (note that in this case a and b are guaranteed to be integers).
// If the standard conversion to double rounds the long value up, e.g. (double) n = b,
// the difference b - n will be positive, indicating that the result needs to be decremented
// to the previous double value a.
double result = n;
long dif = (long) result - n;
return dif <= 0 ? result : Math.nextDown(result);
}
}