001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.commons.lang; 018 019import static org.apache.juneau.commons.utils.Shorts.*; 020 021 022/** 023 * A simple mutable double value. 024 * 025 * <p> 026 * This class extends {@link Holder}<{@link Double}> and provides a convenient way to pass mutable 027 * double references to lambdas, inner classes, or methods. 028 * 029 * <h5 class='section'>Notes:</h5><ul> 030 * <li class='note'> 031 * This class is <b>not thread-safe</b>. 032 * </ul> 033 * 034 * <h5 class='section'>Example:</h5> 035 * <p class='bjava'> 036 * <jc>// Create a double value to track a running sum</jc> 037 * DoubleHolder <jv>sum</jv> = DoubleHolder.<jsm>create</jsm>(); 038 * 039 * <jc>// Use in a lambda to accumulate values</jc> 040 * list.forEach(<jv>x</jv> -> { 041 * <jv>sum</jv>.set(<jv>sum</jv>.get() + <jv>x</jv>.doubleValue()); 042 * }); 043 * 044 * <jsm>log</jsm>(<js>"Total: "</js> + <jv>sum</jv>.get()); 045 * </p> 046 * 047 * <h5 class='section'>See Also:</h5><ul> 048 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauCommonsLang">Lang Package</a> 049 * </ul> 050 */ 051@SuppressWarnings({ 052 "java:S1192" // Duplicated literals (argument/property names) read more clearly inline than as constants 053}) 054public class DoubleHolder extends Holder<Double> { 055 056 /** 057 * Creates a new double value initialized to <c>0.0</c>. 058 * 059 * <h5 class='section'>Example:</h5> 060 * <p class='bjava'> 061 * DoubleHolder <jv>value</jv> = DoubleHolder.<jsm>create</jsm>(); 062 * <jsm>assertEquals</jsm>(0.0, <jv>value</jv>.get()); 063 * </p> 064 * 065 * @return A new double value. 066 */ 067 public static DoubleHolder create() { 068 return of(0.0); 069 } 070 071 /** 072 * Creates a new double value with the specified initial value. 073 * 074 * <h5 class='section'>Example:</h5> 075 * <p class='bjava'> 076 * DoubleHolder <jv>value</jv> = DoubleHolder.<jsm>of</jsm>(3.14159); 077 * <jsm>assertEquals</jsm>(3.14159, <jv>value</jv>.get()); 078 * </p> 079 * 080 * @param value The initial value. Can be <jk>null</jk>. 081 * @return A new double value. 082 */ 083 public static DoubleHolder of(Double value) { 084 return new DoubleHolder(value); 085 } 086 087 /** 088 * Constructor. 089 * 090 * @param value The initial value. Can be <jk>null</jk>. 091 */ 092 public DoubleHolder(Double value) { 093 super(value); 094 } 095 096 /** 097 * Checks if the current value equals the specified value within the given precision. 098 * 099 * <p> 100 * This method handles <jk>null</jk> values safely and performs precision-based equality comparison 101 * using absolute difference. The comparison returns <jk>true</jk> if the absolute difference between 102 * the two values is less than or equal to the specified precision. 103 * 104 * <h5 class='section'>Example:</h5> 105 * <p class='bjava'> 106 * DoubleHolder <jv>value</jv> = DoubleHolder.<jsm>of</jsm>(3.14159); 107 * <jsm>assertTrue</jsm>(<jv>value</jv>.is(3.14, 0.01)); <jc>// Within 0.01</jc> 108 * <jsm>assertFalse</jsm>(<jv>value</jv>.is(3.14, 0.001)); <jc>// Not within 0.001</jc> 109 * 110 * <jc>// Handles null values</jc> 111 * DoubleHolder <jv>empty</jv> = DoubleHolder.<jsm>create</jsm>(); 112 * <jv>empty</jv>.set(<jk>null</jk>); 113 * <jsm>assertFalse</jsm>(<jv>empty</jv>.is(3.14, 0.01)); <jc>// Null doesn't match any value</jc> 114 * 115 * <jc>// Floating-point precision example</jc> 116 * DoubleHolder <jv>calc</jv> = DoubleHolder.<jsm>of</jsm>(0.1 + 0.2); <jc>// Actually 0.30000000000000004</jc> 117 * <jsm>assertTrue</jsm>(<jv>calc</jv>.is(0.3, 0.000001)); <jc>// Handles rounding errors</jc> 118 * </p> 119 * 120 * @param other The value to compare with. 121 * @param precision The maximum allowed difference for equality. Must be non-negative. 122 * @return <jk>true</jk> if the absolute difference between the values is less than or equal to precision. 123 * @throws IllegalArgumentException if precision is negative. 124 */ 125 public boolean is(double other, double precision) { 126 req(precision >= 0, "Precision must be non-negative"); 127 var v = get(); 128 if (v == null) { 129 return false; 130 } 131 return Math.abs(v - other) <= precision; 132 } 133 134 /** 135 * Checks if the current value matches any of the specified values within the given precision. 136 * 137 * <p> 138 * This method handles <jk>null</jk> values safely and performs precision-based equality comparison 139 * for each value using absolute difference. 140 * 141 * <h5 class='section'>Example:</h5> 142 * <p class='bjava'> 143 * DoubleHolder <jv>value</jv> = DoubleHolder.<jsm>of</jsm>(3.14159); 144 * <jsm>assertTrue</jsm>(<jv>value</jv>.isAny(0.01, 2.5, 3.15, 5.0)); <jc>// Matches 3.15 within 0.01</jc> 145 * <jsm>assertFalse</jsm>(<jv>value</jv>.isAny(0.001, 1.0, 2.0, 5.0)); <jc>// No match within 0.001</jc> 146 * 147 * <jc>// Empty array returns false</jc> 148 * <jsm>assertFalse</jsm>(<jv>value</jv>.isAny(0.01)); 149 * </p> 150 * 151 * @param precision The maximum allowed difference for equality. Must be non-negative and is the first parameter. 152 * @param values The values to compare to. Must not be <jk>null</jk>. 153 * @return <jk>true</jk> if the current value matches any of the specified values within the precision. 154 * @throws IllegalArgumentException if precision is negative. 155 */ 156 public boolean isAny(double precision, double...values) { 157 req(precision >= 0, "Precision must be non-negative"); 158 reqnn("values", values); 159 var v = get(); 160 if (v == null) 161 return false; 162 for (var value : values) 163 if (Math.abs(v - value) <= precision) 164 return true; 165 return false; 166 } 167}