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.AssertionUtils.*; 020 021/** 022 * A simple mutable double value. 023 * 024 * <p> 025 * This class extends {@link Value}<{@link Double}> and provides a convenient way to pass mutable 026 * double references to lambdas, inner classes, or methods. 027 * 028 * <h5 class='section'>Notes:</h5><ul> 029 * <li class='note'> 030 * This class is <b>not thread-safe</b>. 031 * </ul> 032 * 033 * <h5 class='section'>Example:</h5> 034 * <p class='bjava'> 035 * <jc>// Create a double value to track a running sum</jc> 036 * DoubleValue <jv>sum</jv> = DoubleValue.<jsm>create</jsm>(); 037 * 038 * <jc>// Use in a lambda to accumulate values</jc> 039 * list.forEach(<jv>x</jv> -> { 040 * <jv>sum</jv>.set(<jv>sum</jv>.get() + <jv>x</jv>.doubleValue()); 041 * }); 042 * 043 * <jsm>log</jsm>(<js>"Total: "</js> + <jv>sum</jv>.get()); 044 * </p> 045 * 046 * <h5 class='section'>See Also:</h5><ul> 047 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauCommonsLang">Lang Package</a> 048 * </ul> 049 */ 050public class DoubleValue extends Value<Double> { 051 052 /** 053 * Creates a new double value initialized to <c>0.0</c>. 054 * 055 * <h5 class='section'>Example:</h5> 056 * <p class='bjava'> 057 * DoubleValue <jv>value</jv> = DoubleValue.<jsm>create</jsm>(); 058 * <jsm>assertEquals</jsm>(0.0, <jv>value</jv>.get()); 059 * </p> 060 * 061 * @return A new double value. 062 */ 063 public static DoubleValue create() { 064 return of(0.0); 065 } 066 067 /** 068 * Creates a new double value with the specified initial value. 069 * 070 * <h5 class='section'>Example:</h5> 071 * <p class='bjava'> 072 * DoubleValue <jv>value</jv> = DoubleValue.<jsm>of</jsm>(3.14159); 073 * <jsm>assertEquals</jsm>(3.14159, <jv>value</jv>.get()); 074 * </p> 075 * 076 * @param value The initial value. 077 * @return A new double value. 078 */ 079 public static DoubleValue of(Double value) { 080 return new DoubleValue(value); 081 } 082 083 /** 084 * Constructor. 085 * 086 * @param value The initial value. 087 */ 088 public DoubleValue(Double value) { 089 super(value); 090 } 091 092 /** 093 * Checks if the current value equals the specified value within the given precision. 094 * 095 * <p> 096 * This method handles <jk>null</jk> values safely and performs precision-based equality comparison 097 * using absolute difference. The comparison returns <jk>true</jk> if the absolute difference between 098 * the two values is less than or equal to the specified precision. 099 * 100 * <h5 class='section'>Example:</h5> 101 * <p class='bjava'> 102 * DoubleValue <jv>value</jv> = DoubleValue.<jsm>of</jsm>(3.14159); 103 * <jsm>assertTrue</jsm>(<jv>value</jv>.is(3.14, 0.01)); <jc>// Within 0.01</jc> 104 * <jsm>assertFalse</jsm>(<jv>value</jv>.is(3.14, 0.001)); <jc>// Not within 0.001</jc> 105 * 106 * <jc>// Handles null values</jc> 107 * DoubleValue <jv>empty</jv> = DoubleValue.<jsm>create</jsm>(); 108 * <jv>empty</jv>.set(<jk>null</jk>); 109 * <jsm>assertFalse</jsm>(<jv>empty</jv>.is(3.14, 0.01)); <jc>// Null doesn't match any value</jc> 110 * 111 * <jc>// Floating-point precision example</jc> 112 * DoubleValue <jv>calc</jv> = DoubleValue.<jsm>of</jsm>(0.1 + 0.2); <jc>// Actually 0.30000000000000004</jc> 113 * <jsm>assertTrue</jsm>(<jv>calc</jv>.is(0.3, 0.000001)); <jc>// Handles rounding errors</jc> 114 * </p> 115 * 116 * @param other The value to compare with. 117 * @param precision The maximum allowed difference for equality. Must be non-negative. 118 * @return <jk>true</jk> if the absolute difference between the values is less than or equal to precision. 119 * @throws IllegalArgumentException if precision is negative. 120 */ 121 public boolean is(double other, double precision) { 122 assertArg(precision >= 0, "Precision must be non-negative"); 123 var v = get(); 124 if (v == null) { 125 return false; 126 } 127 return Math.abs(v - other) <= precision; 128 } 129 130 /** 131 * Checks if the current value matches any of the specified values within the given precision. 132 * 133 * <p> 134 * This method handles <jk>null</jk> values safely and performs precision-based equality comparison 135 * for each value using absolute difference. 136 * 137 * <h5 class='section'>Example:</h5> 138 * <p class='bjava'> 139 * DoubleValue <jv>value</jv> = DoubleValue.<jsm>of</jsm>(3.14159); 140 * <jsm>assertTrue</jsm>(<jv>value</jv>.isAny(0.01, 2.5, 3.15, 5.0)); <jc>// Matches 3.15 within 0.01</jc> 141 * <jsm>assertFalse</jsm>(<jv>value</jv>.isAny(0.001, 1.0, 2.0, 5.0)); <jc>// No match within 0.001</jc> 142 * 143 * <jc>// Empty array returns false</jc> 144 * <jsm>assertFalse</jsm>(<jv>value</jv>.isAny(0.01)); 145 * </p> 146 * 147 * @param precision The maximum allowed difference for equality. Must be non-negative and is the first parameter. 148 * @param values The values to compare to. 149 * @return <jk>true</jk> if the current value matches any of the specified values within the precision. 150 * @throws IllegalArgumentException if precision is negative. 151 */ 152 public boolean isAny(double precision, double...values) { 153 assertArg(precision >= 0, "Precision must be non-negative"); 154 assertArgNotNull("values", values); 155 var v = get(); 156 if (v == null) 157 return false; 158 for (var value : values) 159 if (Math.abs(v - value) <= precision) 160 return true; 161 return false; 162 } 163}