MeasurementRounding.java
/*
* Copyright (c) 2024, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package com.powsybl.openrao.commons;
import java.math.BigDecimal;
import java.math.RoundingMode;
/**
* @author Thomas Bouquet {@literal <thomas.bouquet at rte-france.com>}
*/
public final class MeasurementRounding {
private MeasurementRounding() {
}
/**
* Rounds a measured value with a number of decimals determined from the associated margin with a threshold.
* This helps to increase the decimal precision in case a violation is very narrow (order of magnitude is a negative
* power of ten) and could go unnoticed by the user when reading the results if the rounding is performed with a
* fixed number of decimals (ex: 100.0001 being rounded to 100 would make the 0.0001 violation with respect to a
* threshold of 100 invisible). The rule is as follows:
*
* <ul>
* <li>
* if the margin is positive, there is no violation and no specific rounding is required so the default
* number of decimals is used
* </li>
* <li>
* if the margin is negative but greater than 1 in absolute value, the violation is big enough for the
* rounding not to <i>hide</i> the violation part of the measured value so the default number of decimals is
* used
* </li>
* <li>
* if the margin is negative and strictly lower than 1 in absolute value, the number of required decimals is
* computed with the formula ���-log10(margin)��� and returned, except if it is lower than the default number of
* decimals in which case the latter is returned
* </li>
* </ul>
*
* @param value : the measured value to round
* @param margin : the margin between the measured value and the threshold
* @param defaultDecimals : the default minimum number of decimals used to round the value
* @return rounded value as a BigDecimal
*/
public static BigDecimal roundValueBasedOnMargin(double value, double margin, int defaultDecimals) {
int relevantDecimals;
if (Double.isNaN(margin) || margin >= 0 || margin <= -1) {
relevantDecimals = defaultDecimals;
} else {
// for a number x in [0, 1[, the position of the first decimal which is not a 0 is given by ���-log10(x)���
relevantDecimals = Math.max(defaultDecimals, (int) Math.ceil(-Math.log10(Math.abs(margin))));
}
double boundedValue = Math.min(Double.MAX_VALUE, Math.max(-Double.MAX_VALUE, value));
return BigDecimal.valueOf(boundedValue).setScale(relevantDecimals, RoundingMode.HALF_UP);
}
}