If it is divisible, please calculate up to the point where it is divisible. But if it is not divisible, always find the quotient with the same number of significant digits. I think it will be necessary for an application that displays the calculation result of division like a calculator. I couldn't find a good way on the net, so I thought about it myself (`・ ω ・ ´)
(Added on 2018/07/16) To clarify what I want to achieve, I added a usage example as a GIF below. By the way, this image was taken with a calculator app called Calc Leaf that I developed.
In this way, even if the answer is an infinite decimal, the purpose is to display it properly within the displayable range.
Here are some output examples.
I will omit it here, but if you format the obtained answer with Decimal Format
, the display will look good.
Division.java
import java.math.BigDecimal;
import java.math.RoundingMode;
public class Division {
public static final int SIGNIFICANT_DIGITS = 3; //Number of significant digits
public static void main(String[] args) {
try {
System.out.println(divided("10000", "3")); // 3330
System.out.println(divided("1000", "3")); // 333
System.out.println(divided("100", "3")); // 33.3
System.out.println(divided("10", "3")); // 3.33
System.out.println(divided("1", "3")); // 0.333
System.out.println(divided("1", "30")); // 0.0333
System.out.println(divided("1", "300")); // 0.00333
System.out.println(divided("1", "3000")); // 0.000333
System.out.println(divided("1", "30000")); // 0.0000333
} catch(NumberFormatException nfe) {
System.out.println("Error: Not a number.");
} catch(ArithmeticException ae) {
System.out.println("Error: Impossible to calculate.");
} catch(Exception e) {
System.out.println("Error: Unexpected.");
}
}
public static String divided(String dividend, String divisor) throws Exception {
BigDecimal bdDividend = new BigDecimal(dividend);
BigDecimal bdDivisor = new BigDecimal(divisor);
BigDecimal answer = null;
try {
//If it is divisible, divide it as it is
answer = bdDividend.divide(bdDivisor);
} catch(ArithmeticException arithmeticException) {
//If it is not divisible, calculate a valid scale (in this example, rounding is rounded)
answer = bdDividend.divide(bdDivisor, getSignificantScale(bdDividend, bdDivisor), RoundingMode.HALF_UP);
}
return answer.stripTrailingZeros().toPlainString();
}
public static int getSignificantScale(BigDecimal dividend, BigDecimal divisor) {
//Absolute value excluding trailing zeros after the decimal point
BigDecimal bd1 = dividend.abs().stripTrailingZeros();
BigDecimal bd2 = divisor.abs().stripTrailingZeros();
//Convert to plain character string according to natural numbers
int decimalDigits = bd1.scale() > bd2.scale() ? bd1.scale() : bd2.scale();
if (decimalDigits < 0) decimalDigits = 0;
String s1 = bd1.scaleByPowerOfTen(decimalDigits).toPlainString();
String s2 = bd2.scaleByPowerOfTen(decimalDigits).toPlainString();
//Calculate and return a valid scale
return SIGNIFICANT_DIGITS - (s1.length() - s2.length()) - (s1.compareTo(s2) >= 0 ? 1 : 0);
}
}
You can copy and paste it to Try kotlin as it is.
You can use let
to make val
on one line where you want decimalDigits
, but I stopped it because it's subtle.
Division.kt
import java.math.BigDecimal
import java.math.RoundingMode
const val SIGNIFICANT_DIGITS: Int = 3 //Number of significant digits
fun main(args: Array<String>) {
try {
println(divided("10000", "3")) // 3330
println(divided("1000", "3")) // 333
println(divided("100", "3")) // 33.3
println(divided("10", "3")) // 3.33
println(divided("1", "3")) // 0.333
println(divided("1", "30")) // 0.0333
println(divided("1", "300")) // 0.00333
println(divided("1", "3000")) // 0.000333
println(divided("1", "30000")) // 0.0000333
} catch(nfe: NumberFormatException) {
println("Error: Not a number.")
} catch(ae: ArithmeticException) {
println("Error: Impossible to calculate.")
} catch(e: Exception) {
println("Error: Unexpected.")
}
}
fun divided(dividend: String, divisor: String): String {
val bdDividend = BigDecimal(dividend)
val bdDivisor = BigDecimal(divisor)
val answer = try {
//If it is divisible, divide it as it is
bdDividend.divide(bdDivisor)
} catch(ae: ArithmeticException) {
//If it is not divisible, calculate a valid scale (in this example, rounding is rounded)
bdDividend.divide(bdDivisor, getSignificantScale(bdDividend, bdDivisor), RoundingMode.HALF_UP)
}
return answer.stripTrailingZeros().toPlainString()
}
fun getSignificantScale(dividend: BigDecimal, divisor: BigDecimal): Int {
//Absolute value excluding trailing zeros after the decimal point
val bd1 = dividend.abs().stripTrailingZeros()
val bd2 = divisor.abs().stripTrailingZeros()
//Convert to plain character string according to natural numbers
var decimalDigits = if (bd1.scale() > bd2.scale()) bd1.scale() else bd2.scale()
if (decimalDigits < 0) decimalDigits = 0
val s1 = bd1.scaleByPowerOfTen(decimalDigits).toPlainString()
val s2 = bd2.scaleByPowerOfTen(decimalDigits).toPlainString()
//Calculate and return a valid scale
return SIGNIFICANT_DIGITS - (s1.length - s2.length) - (if (s1 >= s2) 1 else 0)
}
You can test the above code yourself and use it as you like. The test code I conducted has been uploaded to github (released in response to comments from kaizen_nagoya), but We verified about 50 patterns based on the relationship between the number to be divided and the number to be divided, the position of the decimal point, positive / negative, and exponential notation, and cleared the requirements. However, it is undeniable that it lacks objectivity because I was doing it alone (´ ・ ω ・ `) So, those who review and re-verify are welcome! Also, if you have information on libraries or reference sites that are doing the same thing, I would be very happy if you could let me know! (I thought about it now, but it may be listed in the math dictionary. I don't have it.)
getSignificantScale
is actually used in the calculator app CalcLeaf that I developed, but it's actually a little There is a difference in the implementation.
The point is that "0 is returned if the calculation result of a valid scale is negative" as shown below.
Sample.java
int significantScale = SIGNIFICANT_DIGITS - (s1.length() - s2.length()) - (s1.compareTo(s2) >= 0 ? 1 : 0);
return significantScale < 0 ? 0 : significantScale;
In the case of a calculator, for example, if the answer of 1,000,000,000,000 ÷ 3 becomes 333,333,333,330, it feels unpleasant, so Calc Leaf adjusts it as a specification.
I wish I could do it as standard with Java API. I wonder if people in Java will consider it if they see it.
Recommended Posts