All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.opentelemetry.sdk.metrics.internal.aggregator.Base2ExponentialHistogramIndexer Maven / Gradle / Ivy

There is a newer version: 1.44.1
Show newest version
/*
 * Copyright The OpenTelemetry Authors
 * SPDX-License-Identifier: Apache-2.0
 */

package io.opentelemetry.sdk.metrics.internal.aggregator;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

final class Base2ExponentialHistogramIndexer {

  private static final Map cache =
      new ConcurrentHashMap<>();

  /** Bit mask used to isolate exponent of IEEE 754 double precision number. */
  private static final long EXPONENT_BIT_MASK = 0x7FF0000000000000L;

  /** Bit mask used to isolate the significand of IEEE 754 double precision number. */
  private static final long SIGNIFICAND_BIT_MASK = 0xFFFFFFFFFFFFFL;

  /** Bias used in representing the exponent of IEEE 754 double precision number. */
  private static final int EXPONENT_BIAS = 1023;

  /**
   * The number of bits used to represent the significand of IEEE 754 double precision number,
   * excluding the implicit bit.
   */
  private static final int SIGNIFICAND_WIDTH = 52;

  /** The number of bits used to represent the exponent of IEEE 754 double precision number. */
  private static final int EXPONENT_WIDTH = 11;

  private static final double LOG_BASE2_E = 1D / Math.log(2);

  private final int scale;
  private final double scaleFactor;

  private Base2ExponentialHistogramIndexer(int scale) {
    this.scale = scale;
    this.scaleFactor = computeScaleFactor(scale);
  }

  /** Get an indexer for the given scale. Indexers are cached and reused for performance. */
  static Base2ExponentialHistogramIndexer get(int scale) {
    return cache.computeIfAbsent(scale, unused -> new Base2ExponentialHistogramIndexer(scale));
  }

  /**
   * Compute the index for the given value.
   *
   * 

The algorithm to retrieve the index is specified in the OpenTelemetry * specification. * * @param value Measured value (must be non-zero). * @return the index of the bucket which the value maps to. */ int computeIndex(double value) { double absValue = Math.abs(value); // For positive scales, compute the index by logarithm, which is simpler but may be // inaccurate near bucket boundaries if (scale > 0) { return getIndexByLogarithm(absValue); } // For scale zero, compute the exact index by extracting the exponent if (scale == 0) { return mapToIndexScaleZero(absValue); } // For negative scales, compute the exact index by extracting the exponent and shifting it to // the right by -scale return mapToIndexScaleZero(absValue) >> -scale; } /** * Compute the bucket index using a logarithm based approach. * * @see All * Scales: Use the Logarithm Function */ private int getIndexByLogarithm(double value) { return (int) Math.ceil(Math.log(value) * scaleFactor) - 1; } /** * Compute the exact bucket index for scale zero by extracting the exponent. * * @see Scale * Zero: Extract the Exponent */ private static int mapToIndexScaleZero(double value) { long rawBits = Double.doubleToLongBits(value); long rawExponent = (rawBits & EXPONENT_BIT_MASK) >> SIGNIFICAND_WIDTH; long rawSignificand = rawBits & SIGNIFICAND_BIT_MASK; if (rawExponent == 0) { rawExponent -= Long.numberOfLeadingZeros(rawSignificand - 1) - EXPONENT_WIDTH - 1; } int ieeeExponent = (int) (rawExponent - EXPONENT_BIAS); if (rawSignificand == 0) { return ieeeExponent - 1; } return ieeeExponent; } private static double computeScaleFactor(int scale) { return Math.scalb(LOG_BASE2_E, scale); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy