Java / Kotlin: Calculez le quotient en spécifiant le nombre de nombres valides lorsqu'il n'est pas divisible par la division (division) de BigDecimal.

S'il est divisible, veuillez calculer jusqu'au point où il est divisible. Mais s'il n'est pas divisible, trouvez toujours le quotient avec le même nombre de chiffres valides. Je pense qu'il sera nécessaire pour une application qui affiche le résultat du calcul de la division comme une calculatrice. Je n'ai pas pu trouver un bon moyen sur le net, alors j'y ai pensé moi-même (`・ ω ・ ´)

(Ajouté le 16/07/2018) J'ai ajouté un exemple d'utilisation au format GIF ci-dessous pour indiquer clairement ce que vous souhaitez atteindre. En passant, cette image a été prise avec une application de calcul appelée Calc Leaf que j'ai développée.

De cette manière, même si la réponse est un nombre infini, le but est de l'afficher correctement dans la plage affichable.

Pour Java

Voici quelques exemples de sortie. Je vais l'omettre ici, mais si vous formatez la réponse obtenue avec Decimal Format, l'affichage sera beau.

Division.java


import java.math.BigDecimal;
import java.math.RoundingMode;

public class Division {
	
	public static final int SIGNIFICANT_DIGITS = 3; //Nombre de chiffres valides
	
	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 {
			//S'il est divisible, divisez-le tel quel
			answer = bdDividend.divide(bdDivisor);
		} catch(ArithmeticException arithmeticException) {
			//S'il n'est pas divisible, calculez une échelle valide (dans cet exemple, l'arrondi est arrondi)
			answer = bdDividend.divide(bdDivisor, getSignificantScale(bdDividend, bdDivisor), RoundingMode.HALF_UP);
		}
		return answer.stripTrailingZeros().toPlainString();
	}
	
	public static int getSignificantScale(BigDecimal dividend, BigDecimal divisor) {
		//Valeur absolue à l'exclusion des zéros de fin après la virgule décimale
		BigDecimal bd1 = dividend.abs().stripTrailingZeros();
		BigDecimal bd2 = divisor.abs().stripTrailingZeros();
		//Créer une chaîne de caractères simple selon un nombre naturel
		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();
		//Calculer et renvoyer une échelle valide
		return SIGNIFICANT_DIGITS - (s1.length() - s2.length()) - (s1.compareTo(s2) >= 0 ? 1 : 0);
	}
}

Pour Kotlin

Vous pouvez le copier et le coller dans Try kotlin tel quel. Vous pouvez utiliser let pour faire val sur une ligne où vous voulez decimalDigits, mais je l'ai arrêté parce que c'est subtil.

Division.kt


import java.math.BigDecimal
import java.math.RoundingMode

const val SIGNIFICANT_DIGITS: Int = 3 //Nombre de chiffres valides

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 {
        //S'il est divisible, divisez-le tel quel
        bdDividend.divide(bdDivisor)
    } catch(ae: ArithmeticException) {
        //S'il n'est pas divisible, calculez une échelle valide (dans cet exemple, l'arrondi est arrondi)
        bdDividend.divide(bdDivisor, getSignificantScale(bdDividend, bdDivisor), RoundingMode.HALF_UP)
    }
    return answer.stripTrailingZeros().toPlainString()
}

fun getSignificantScale(dividend: BigDecimal, divisor: BigDecimal): Int {
    //Valeur absolue à l'exclusion des zéros de fin après la virgule décimale
    val bd1 = dividend.abs().stripTrailingZeros()
    val bd2 = divisor.abs().stripTrailingZeros()
    //Créer une chaîne de caractères simple selon un nombre naturel
    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()
    //Calculer et renvoyer une échelle valide
    return SIGNIFICANT_DIGITS - (s1.length - s2.length) - (if (s1 >= s2) 1 else 0)
}

Utilisez à vos risques et périls

Vous pouvez tester vous-même le code ci-dessus et l'utiliser comme vous le souhaitez. Le code de test que j'ai effectué a été téléchargé sur ~~ github (publié en réponse aux commentaires de kaizen_nagoya), Nous avons vérifié environ 50 modèles en fonction de la relation entre le nombre à diviser et le nombre à diviser, la position de la virgule décimale, positive / négative, et s'il s'agit ou non d'une notation exponentielle, et effacé les exigences. Cependant, il est indéniable qu'il manque d'objectivité car je le faisais seul (´ ・ ω ・ `) Donc, ceux qui révisent et revérifient sont les bienvenus! Aussi, si vous avez des informations sur des bibliothèques ou des sites de référence qui font la même chose, je serais très heureux si vous pouviez me le faire savoir! (J'y ai réfléchi maintenant, mais il est peut-être répertorié dans le dictionnaire mathématique. Je ne l'ai pas.)

Vous pouvez l'ajuster en tenant compte de l'affichage du résultat du calcul.

getSignificantScale est en fait utilisé dans mon application de calculatrice développée CalcLeaf, mais c'est en fait un peu Il y a une différence dans la mise en œuvre. Le fait est que "0 est renvoyé si le résultat du calcul d'une échelle valide est négatif" comme indiqué ci-dessous.

Sample.java


int significantScale = SIGNIFICANT_DIGITS - (s1.length() - s2.length()) - (s1.compareTo(s2) >= 0 ? 1 : 0);
return significantScale < 0 ? 0 : significantScale;

Dans le cas d'une calculatrice, par exemple, si la réponse de 1 000 000 000 000 ÷ 3 devient 333 333 333 330, c'est désagréable, donc Calc Leaf l'a ajustée comme spécification.

Impressions

J'aurais aimé pouvoir le faire en standard avec l'API Java. Je me demande si les gens en Java y réfléchiront s'ils le voient.

Recommended Posts

Java / Kotlin: Calculez le quotient en spécifiant le nombre de nombres valides lorsqu'il n'est pas divisible par la division (division) de BigDecimal.
[Java] Calculer le jour à partir de la date (ne pas utiliser la classe Calendar)
Veuillez noter la division (division) de java kotlin Int et Int
L'idée de couper quand l'erreur n'est pas résolue
Comptez le nombre de chiffres après la virgule décimale en Java
[Rails] Lorsque le changement de disposition du dispositif n'est pas reflété
Changer la version de java installée par SDKMAN lors du déplacement de répertoires
Méthode d'enquête lorsque le processeur du serveur exécutant java est lourd