From db4c1ba095023636f4bb766e1c5962c40ca4c60c Mon Sep 17 00:00:00 2001 From: Amey Jadiye Date: Tue, 26 Feb 2019 23:46:06 +0530 Subject: [PATCH 1/4] TEXT-126: Adding Sorensen-Dice similarity algorithm --- .../similarity/SorensenDiceSimilarity.java | 146 ++++++++++++++++++ .../SorensenDiceSimilarityTest.java | 94 +++++++++++ 2 files changed, 240 insertions(+) create mode 100644 src/main/java/org/apache/commons/text/similarity/SorensenDiceSimilarity.java create mode 100644 src/test/java/org/apache/commons/text/similarity/SorensenDiceSimilarityTest.java diff --git a/src/main/java/org/apache/commons/text/similarity/SorensenDiceSimilarity.java b/src/main/java/org/apache/commons/text/similarity/SorensenDiceSimilarity.java new file mode 100644 index 0000000000..3121bdc2f1 --- /dev/null +++ b/src/main/java/org/apache/commons/text/similarity/SorensenDiceSimilarity.java @@ -0,0 +1,146 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.commons.text.similarity; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.function.Function; + +import org.apache.commons.lang3.StringUtils; + +/** + * A similarity algorithm indicating the percentage of matched characters + * between two character sequences. + * + *

+ * The Sørensen-Dice coefficient is a statistic used for comparing the + * similarity of two samples. It was independently developed by the botanists + * Thorvald Sørensen and Lee Raymond Dice, who published in 1948 and 1945 + * respectively. The index is known by several other names, especially + * Sørensen-Dice index, Sørensen index and Dice's coefficient. Other + * variations include the "similarity coefficient" or "index", such as Dice + * similarity coefficient (DSC). + *

+ * + *

+ * This implementation is based on the Sørensen-Dice similarity algorithm + * from + * http://en.wikipedia.org/wiki/S%C3%B8rensen%E2%80%93Dice_coefficient. + * + * + *

+ * + * @since 1.7 + */ +public class SorensenDiceSimilarity implements SimilarityScore { + + /** + * For shifting bigrams to fit in single integer. + */ + private static final int SHIFT_NUMBER = 16; + + /** + * Converter function for conversion of string to bigrams. + */ + private final Function> converter = new SorensenDiceConverter(); + + /** + * Measures the overlap of two sets created from a pair of character sequences. + * {@link OverlapSimilarity}} + */ + private final IntersectionSimilarity similarity = new IntersectionSimilarity<>(this.converter); + + /** + * Calculates Sorensen-Dice Similarity of two character sequences passed as + * input. + * + *
+     * similarity.apply(null, null)                 = IllegalArgumentException
+     * similarity.apply("foo", null)                = IllegalArgumentException
+     * similarity.apply(null, "foo")                = IllegalArgumentException
+     * similarity.apply("night", "nacht")           = 0.25
+     * similarity.apply("", "")                     = 1.0
+     * similarity.apply("foo", "foo")               = 1.0
+     * similarity.apply("foo", "foo ")              = 0.8
+     * similarity.apply("foo", "foo ")              = 0.66
+     * similarity.apply("foo", " foo ")             = 0.66
+     * similarity.apply("foo", " foo")              = 0.66
+     * similarity.apply("", "a")                    = 0.0
+     * similarity.apply("aaapppp", "")              = 0.0
+     * similarity.apply("frog", "fog")              = 0.4
+     * similarity.apply("fly", "ant")               = 0.0
+     * similarity.apply("elephant", "hippo")        = 0.0
+     * similarity.apply("hippo", "elephant")        = 0.0
+     * similarity.apply("hippo", "zzzzzzzz")        = 0.0
+     * similarity.apply("hello", "hallo")           = 0.5
+     * similarity.apply("ABC Corporation", "ABC Corp") = 0.7
+     * similarity.apply("D N H Enterprises Inc", "D & H Enterprises, Inc.") = 0.74
+     * similarity.apply("My Gym Children's Fitness Center", "My Gym. Childrens Fitness") = 0.81
+     * similarity.apply("PENNSYLVANIA", "PENNCISYLVNIA") = 0.69
+     * 
+ * + * @param left the first CharSequence, must not be null + * @param right the second CharSequence, must not be null + * @return result similarity + * @throws IllegalArgumentException if either CharSequence input is {@code null} + */ + @Override + public Double apply(final CharSequence left, final CharSequence right) { + + if (left == null || right == null) { + throw new IllegalArgumentException("CharSequences must not be null"); + } + + if (StringUtils.equals(left, right)) { + return 1d; + } + + // if bigram is not formed out of any given string, clearly both are not similar. + if (left.length() < 2 || right.length() < 2) { + return 0d; + } + + IntersectionResult overlap = similarity.apply(left, right); + + final int total = overlap.getSizeA() + overlap.getSizeB(); + final long intersection = overlap.getIntersection(); + + return (2.0d * intersection) / total; + } + + /** + * Converter class for creating Bigrams for SorensenDice similarity. + */ + private static class SorensenDiceConverter implements Function> { + @Override + public Collection apply(CharSequence cs) { + final int length = cs.length(); + final List list = new ArrayList<>(length); + if (length > 1) { + char ch2 = cs.charAt(0); + for (int i = 1; i < length; i++) { + final char ch1 = ch2; + ch2 = cs.charAt(i); + list.add(Integer.valueOf((ch1 << SHIFT_NUMBER) | ch2)); + } + } + return list; + } + } +} diff --git a/src/test/java/org/apache/commons/text/similarity/SorensenDiceSimilarityTest.java b/src/test/java/org/apache/commons/text/similarity/SorensenDiceSimilarityTest.java new file mode 100644 index 0000000000..3d59823ea0 --- /dev/null +++ b/src/test/java/org/apache/commons/text/similarity/SorensenDiceSimilarityTest.java @@ -0,0 +1,94 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.commons.text.similarity; + +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +/** + * Unit tests for {@link SorensenDicesSimilarity}. + */ +public class SorensenDiceSimilarityTest { + + private static SorensenDiceSimilarity similarity; + + @BeforeAll + public static void setUp() { + similarity = new SorensenDiceSimilarity(); + } + + @Test + public void test() { + assertEquals(0.25d, similarity.apply("night", "nacht")); + } + + @Test + public void testGetSorensenDicesSimilarity_StringString() { + + assertEquals(1d, similarity.apply("", "")); + assertEquals(0d, similarity.apply("", "a")); + assertEquals(0d, similarity.apply("a", "")); + assertEquals(1d, similarity.apply("a", "a")); + assertEquals(0d, similarity.apply("a", "b")); + assertEquals(1.0d, similarity.apply("foo", "foo")); + assertEquals(0.8d, similarity.apply("foo", "foo ")); + assertEquals(0.4d, similarity.apply("frog", "fog")); + assertEquals(0.0d, similarity.apply("fly", "ant")); + assertEquals(0.0d, similarity.apply("elephant", "hippo")); + assertEquals(0.0d, similarity.apply("hippo", "elephant")); + assertEquals(0.0d, similarity.apply("hippo", "zzzzzzzz")); + assertEquals(0.5d, similarity.apply("hello", "hallo")); + assertEquals(0.7d, round(similarity.apply("ABC Corporation", "ABC Corp"), 1)); + assertEquals(0.7d, round(similarity.apply("D N H Enterprises Inc", "D & H Enterprises, Inc."), 1)); + assertEquals(0.8d, + round(similarity.apply("My Gym Children's Fitness Center", "My Gym. Childrens Fitness"), 1)); + assertEquals(0.7d, round(similarity.apply("PENNSYLVANIA", "PENNCISYLVNIA"), 1)); + assertEquals(0.9d, round(similarity.apply("/opt/software1", "/opt/software2"), 1)); + assertEquals(0.6d, round(similarity.apply("aaabcd", "aaacdb"), 1)); + assertEquals(0.6d, round(similarity.apply("John Horn", "John Hopkins"), 1)); + + } + + @Test + public void testGetSorensenDicesSimilarity_NullNull() { + assertThatIllegalArgumentException().isThrownBy(() -> { + similarity.apply(null, null); + }); + } + + @Test + public void testGetSorensenDicesSimilarity_StringNull() { + assertThatIllegalArgumentException().isThrownBy(() -> { + similarity.apply(" ", null); + }); + } + + @Test + public void testGetSorensenDicesSimilarity_NullString() { + assertThatIllegalArgumentException().isThrownBy(() -> { + similarity.apply(null, "clear"); + }); + } + + public static double round(double value, int precision) { + int scale = (int) Math.pow(10, precision); + return (double) Math.round(value * scale) / scale; + } +} From 7a683a37791073bfcebbaf46498be976a2472fff Mon Sep 17 00:00:00 2001 From: "Bruno P. Kinoshita" Date: Sun, 27 Oct 2024 15:07:46 +0100 Subject: [PATCH 2/4] TEXT-126: WIP first IDE warnings... --- .../similarity/SorensenDiceSimilarity.java | 30 ++++++++----------- .../SorensenDiceSimilarityTest.java | 20 +++++-------- 2 files changed, 19 insertions(+), 31 deletions(-) diff --git a/src/main/java/org/apache/commons/text/similarity/SorensenDiceSimilarity.java b/src/main/java/org/apache/commons/text/similarity/SorensenDiceSimilarity.java index 3121bdc2f1..2ec8227851 100644 --- a/src/main/java/org/apache/commons/text/similarity/SorensenDiceSimilarity.java +++ b/src/main/java/org/apache/commons/text/similarity/SorensenDiceSimilarity.java @@ -16,37 +16,31 @@ */ package org.apache.commons.text.similarity; +import org.apache.commons.lang3.StringUtils; + import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.function.Function; -import org.apache.commons.lang3.StringUtils; - /** * A similarity algorithm indicating the percentage of matched characters * between two character sequences. * - *

- * The Sørensen-Dice coefficient is a statistic used for comparing the + *

The Sørensen-Dice coefficient is a statistic used for comparing the * similarity of two samples. It was independently developed by the botanists - * Thorvald Sørensen and Lee Raymond Dice, who published in 1948 and 1945 + * Thorvald Sørensen and Lee Raymond Dice, who published in 1948 and 1945 * respectively. The index is known by several other names, especially - * Sørensen-Dice index, Sørensen index and Dice's coefficient. Other + * Sørensen-Dice index, Sørensen index and Dice's coefficient. Other * variations include the "similarity coefficient" or "index", such as Dice - * similarity coefficient (DSC). - *

+ * similarity coefficient (DSC).

* - *

- * This implementation is based on the Sørensen-Dice similarity algorithm + *

This implementation is based on the Sørensen-Dice similarity algorithm * from - * http://en.wikipedia.org/wiki/S%C3%B8rensen%E2%80%93Dice_coefficient. - * - * - *

+ * "https://en.wikipedia.org/wiki/Dice-S%C3%B8rensen_coefficient"> + * https://en.wikipedia.org/wiki/Dice-S%C3%B8rensen_coefficient.

* - * @since 1.7 + * @since 1.13 */ public class SorensenDiceSimilarity implements SimilarityScore { @@ -62,7 +56,7 @@ public class SorensenDiceSimilarity implements SimilarityScore { /** * Measures the overlap of two sets created from a pair of character sequences. - * {@link OverlapSimilarity}} + * {@link IntersectionSimilarity}} */ private final IntersectionSimilarity similarity = new IntersectionSimilarity<>(this.converter); @@ -137,7 +131,7 @@ public Collection apply(CharSequence cs) { for (int i = 1; i < length; i++) { final char ch1 = ch2; ch2 = cs.charAt(i); - list.add(Integer.valueOf((ch1 << SHIFT_NUMBER) | ch2)); + list.add((ch1 << SHIFT_NUMBER) | ch2); } } return list; diff --git a/src/test/java/org/apache/commons/text/similarity/SorensenDiceSimilarityTest.java b/src/test/java/org/apache/commons/text/similarity/SorensenDiceSimilarityTest.java index 3d59823ea0..6ac9178031 100644 --- a/src/test/java/org/apache/commons/text/similarity/SorensenDiceSimilarityTest.java +++ b/src/test/java/org/apache/commons/text/similarity/SorensenDiceSimilarityTest.java @@ -16,14 +16,14 @@ */ package org.apache.commons.text.similarity; -import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; -import static org.junit.jupiter.api.Assertions.assertEquals; - import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + /** - * Unit tests for {@link SorensenDicesSimilarity}. + * Unit tests for {@link SorensenDiceSimilarity}. */ public class SorensenDiceSimilarityTest { @@ -68,23 +68,17 @@ public void testGetSorensenDicesSimilarity_StringString() { @Test public void testGetSorensenDicesSimilarity_NullNull() { - assertThatIllegalArgumentException().isThrownBy(() -> { - similarity.apply(null, null); - }); + assertThrows(IllegalArgumentException.class, () -> similarity.apply(null, null)); } @Test public void testGetSorensenDicesSimilarity_StringNull() { - assertThatIllegalArgumentException().isThrownBy(() -> { - similarity.apply(" ", null); - }); + assertThrows(IllegalArgumentException.class, () -> similarity.apply(" ", null)); } @Test public void testGetSorensenDicesSimilarity_NullString() { - assertThatIllegalArgumentException().isThrownBy(() -> { - similarity.apply(null, "clear"); - }); + assertThrows(IllegalArgumentException.class, () -> similarity.apply(null, "clear")); } public static double round(double value, int precision) { From 2401af9cf84b17e99e480473d6485f79ec532b25 Mon Sep 17 00:00:00 2001 From: "Bruno P. Kinoshita" Date: Sun, 27 Oct 2024 16:00:07 +0100 Subject: [PATCH 3/4] TEXT-126: WIP Add all javadoc examples to unit test, and fix existing mistakes --- .../similarity/SorensenDiceSimilarity.java | 9 +++-- .../SorensenDiceSimilarityTest.java | 33 +++++++++++-------- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/apache/commons/text/similarity/SorensenDiceSimilarity.java b/src/main/java/org/apache/commons/text/similarity/SorensenDiceSimilarity.java index 2ec8227851..4f27e80ade 100644 --- a/src/main/java/org/apache/commons/text/similarity/SorensenDiceSimilarity.java +++ b/src/main/java/org/apache/commons/text/similarity/SorensenDiceSimilarity.java @@ -72,9 +72,8 @@ public class SorensenDiceSimilarity implements SimilarityScore { * similarity.apply("", "") = 1.0 * similarity.apply("foo", "foo") = 1.0 * similarity.apply("foo", "foo ") = 0.8 - * similarity.apply("foo", "foo ") = 0.66 - * similarity.apply("foo", " foo ") = 0.66 - * similarity.apply("foo", " foo") = 0.66 + * similarity.apply("foo", " foo") = 0.8 + * similarity.apply("foo", " foo ") ≈ 0.66… * similarity.apply("", "a") = 0.0 * similarity.apply("aaapppp", "") = 0.0 * similarity.apply("frog", "fog") = 0.4 @@ -83,9 +82,9 @@ public class SorensenDiceSimilarity implements SimilarityScore { * similarity.apply("hippo", "elephant") = 0.0 * similarity.apply("hippo", "zzzzzzzz") = 0.0 * similarity.apply("hello", "hallo") = 0.5 - * similarity.apply("ABC Corporation", "ABC Corp") = 0.7 + * similarity.apply("ABC Corporation", "ABC Corp") ≈ 0.66… * similarity.apply("D N H Enterprises Inc", "D & H Enterprises, Inc.") = 0.74 - * similarity.apply("My Gym Children's Fitness Center", "My Gym. Childrens Fitness") = 0.81 + * similarity.apply("My Gym Children's Fitness Center", "My Gym. Childrens Fitness") = 0.76 * similarity.apply("PENNSYLVANIA", "PENNCISYLVNIA") = 0.69 * * diff --git a/src/test/java/org/apache/commons/text/similarity/SorensenDiceSimilarityTest.java b/src/test/java/org/apache/commons/text/similarity/SorensenDiceSimilarityTest.java index 6ac9178031..b26e406bd8 100644 --- a/src/test/java/org/apache/commons/text/similarity/SorensenDiceSimilarityTest.java +++ b/src/test/java/org/apache/commons/text/similarity/SorensenDiceSimilarityTest.java @@ -19,6 +19,9 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import java.math.RoundingMode; +import java.text.DecimalFormat; + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -40,8 +43,7 @@ public void test() { } @Test - public void testGetSorensenDicesSimilarity_StringString() { - + public void testGetSorensenDiceSimilarity_StringString() { assertEquals(1d, similarity.apply("", "")); assertEquals(0d, similarity.apply("", "a")); assertEquals(0d, similarity.apply("a", "")); @@ -49,21 +51,23 @@ public void testGetSorensenDicesSimilarity_StringString() { assertEquals(0d, similarity.apply("a", "b")); assertEquals(1.0d, similarity.apply("foo", "foo")); assertEquals(0.8d, similarity.apply("foo", "foo ")); + assertEquals(0.8d, similarity.apply("foo", " foo")); + assertEquals(0.66d, printTwoDecimals(similarity.apply("foo", " foo "))); assertEquals(0.4d, similarity.apply("frog", "fog")); assertEquals(0.0d, similarity.apply("fly", "ant")); assertEquals(0.0d, similarity.apply("elephant", "hippo")); assertEquals(0.0d, similarity.apply("hippo", "elephant")); assertEquals(0.0d, similarity.apply("hippo", "zzzzzzzz")); + assertEquals(0.25d, similarity.apply("night", "nacht")); assertEquals(0.5d, similarity.apply("hello", "hallo")); - assertEquals(0.7d, round(similarity.apply("ABC Corporation", "ABC Corp"), 1)); - assertEquals(0.7d, round(similarity.apply("D N H Enterprises Inc", "D & H Enterprises, Inc."), 1)); - assertEquals(0.8d, - round(similarity.apply("My Gym Children's Fitness Center", "My Gym. Childrens Fitness"), 1)); - assertEquals(0.7d, round(similarity.apply("PENNSYLVANIA", "PENNCISYLVNIA"), 1)); - assertEquals(0.9d, round(similarity.apply("/opt/software1", "/opt/software2"), 1)); - assertEquals(0.6d, round(similarity.apply("aaabcd", "aaacdb"), 1)); - assertEquals(0.6d, round(similarity.apply("John Horn", "John Hopkins"), 1)); - + assertEquals(0.0d, similarity.apply("aaapppp", "")); + assertEquals(0.66d, printTwoDecimals(similarity.apply("ABC Corporation", "ABC Corp"))); + assertEquals(0.73d, printTwoDecimals(similarity.apply("D N H Enterprises Inc", "D & H Enterprises, Inc."))); + assertEquals(0.76d, printTwoDecimals(similarity.apply("My Gym Children's Fitness Center", "My Gym. Childrens Fitness"))); + assertEquals(0.69d, printTwoDecimals(similarity.apply("PENNSYLVANIA", "PENNCISYLVNIA"))); + assertEquals(0.92d, printTwoDecimals(similarity.apply("/opt/software1", "/opt/software2"))); + assertEquals(0.60d, printTwoDecimals(similarity.apply("aaabcd", "aaacdb"))); + assertEquals(0.63d, printTwoDecimals(similarity.apply("John Horn", "John Hopkins"))); } @Test @@ -81,8 +85,9 @@ public void testGetSorensenDicesSimilarity_NullString() { assertThrows(IllegalArgumentException.class, () -> similarity.apply(null, "clear")); } - public static double round(double value, int precision) { - int scale = (int) Math.pow(10, precision); - return (double) Math.round(value * scale) / scale; + public static double printTwoDecimals(double value) { + DecimalFormat df = new DecimalFormat("#.##"); + df.setRoundingMode(RoundingMode.DOWN); + return Double.parseDouble(df.format(value)); } } From e8b93705d0041e745e4b7087d3e5eb9f412c38a6 Mon Sep 17 00:00:00 2001 From: "Bruno P. Kinoshita" Date: Sun, 27 Oct 2024 16:08:04 +0100 Subject: [PATCH 4/4] TEXT-126: WIP fixing checkstyle order --- .../text/similarity/SorensenDiceSimilarity.java | 6 +++--- .../similarity/SorensenDiceSimilarityTest.java | 14 ++++++++++---- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/apache/commons/text/similarity/SorensenDiceSimilarity.java b/src/main/java/org/apache/commons/text/similarity/SorensenDiceSimilarity.java index 4f27e80ade..e15cfe0958 100644 --- a/src/main/java/org/apache/commons/text/similarity/SorensenDiceSimilarity.java +++ b/src/main/java/org/apache/commons/text/similarity/SorensenDiceSimilarity.java @@ -16,13 +16,13 @@ */ package org.apache.commons.text.similarity; -import org.apache.commons.lang3.StringUtils; - import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.function.Function; +import org.apache.commons.lang3.StringUtils; + /** * A similarity algorithm indicating the percentage of matched characters * between two character sequences. @@ -120,7 +120,7 @@ public Double apply(final CharSequence left, final CharSequence right) { /** * Converter class for creating Bigrams for SorensenDice similarity. */ - private static class SorensenDiceConverter implements Function> { + private static final class SorensenDiceConverter implements Function> { @Override public Collection apply(CharSequence cs) { final int length = cs.length(); diff --git a/src/test/java/org/apache/commons/text/similarity/SorensenDiceSimilarityTest.java b/src/test/java/org/apache/commons/text/similarity/SorensenDiceSimilarityTest.java index b26e406bd8..09c852d0d4 100644 --- a/src/test/java/org/apache/commons/text/similarity/SorensenDiceSimilarityTest.java +++ b/src/test/java/org/apache/commons/text/similarity/SorensenDiceSimilarityTest.java @@ -16,14 +16,14 @@ */ package org.apache.commons.text.similarity; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import java.math.RoundingMode; import java.text.DecimalFormat; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; /** * Unit tests for {@link SorensenDiceSimilarity}. @@ -85,6 +85,12 @@ public void testGetSorensenDicesSimilarity_NullString() { assertThrows(IllegalArgumentException.class, () -> similarity.apply(null, "clear")); } + /** + * Format the double to two decimal places rounding it down. + * + * @param value the double value + * @return double formatted to two places rounding it down + */ public static double printTwoDecimals(double value) { DecimalFormat df = new DecimalFormat("#.##"); df.setRoundingMode(RoundingMode.DOWN);