How not to lose the decimals when doing "longProperty1.divide(longProperty2)"?

Asked

Viewed 155 times

1

What I want to do is very simple, I just don’t know how to "do it right" in Javafx:

I have two Longproperty (num1 and num2) and a Doubleproperty (resultado), where this Doubleproperty resultado must contain - always updated - the value of the num1 for num2.

I can already do it with the code resultado.bind(num1.divide(num2));, but the problem is thus I lose the precision of the decimal places, and I need the decimal places.

From what I understand, the problem is that by num1.divide(num2) the method divide returns a LongBinding (because the divided variables are LongPropertys) instead of returning a DoubleBinding, and it looks like this is it LongBinding that eliminates decimal places.

I tried to make num1 and num2 also be DoublePropertyand make num1.divide(num2); and it worked: the method divide returned a DoubleBinding which preserved the decimal places in the resultado. But I want you to num1 and num2are LongPropertys even, then how to do?

I managed to get around the problem with a gambiarra that shows what I need:

import javafx.beans.binding.DoubleBinding;
import javafx.beans.binding.NumberBinding;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.LongProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleLongProperty;

public class DoubleBindingEmDivisaoDeLong {

    public static void main(String[] args) {
        teste1_divisaoPerdeAsCasasDecimais();
        teste2_divisaoMantemAsCasasDecimais();
    }

    private static void teste1_divisaoPerdeAsCasasDecimais() {
        final DoubleProperty resultado = new SimpleDoubleProperty(0.0);
        final LongProperty num1 = new SimpleLongProperty(45);
        final LongProperty num2 = new SimpleLongProperty(7);
        NumberBinding divide = num1.divide(num2); // O método divide Retorna um "LongBinding" (veja a linha abaixo para confirmar)
        System.out.println(divide);// Imprime: "LongBinding [invalid]"
        resultado.bind(divide);

        System.out.println(resultado.get()); // Imprime: "6.0" ao invés de "6.428571428571429" (perdeu as casas decimais)
    }

    private static void teste2_divisaoMantemAsCasasDecimais() {
        final DoubleProperty resultado = new SimpleDoubleProperty(0.0);
        final LongProperty num1 = new SimpleLongProperty(45);
        final LongProperty num2 = new SimpleLongProperty(7);

        final DoubleProperty num1Double = new SimpleDoubleProperty(0.0);
        num1Double.bind(num1);
        final DoubleProperty num2Double = new SimpleDoubleProperty(0.0);
        num2Double.bind(num2);

        DoubleBinding divide = num1Double.divide(num2Double); // O método divide Retorna um "DoubleBinding"
        resultado.bind(divide);

        System.out.println(resultado.get()); // Imprime "6.428571428571429" como desejado
    }
}

Note that in the code above, what makes it work in test2 is the fact that I created num1Double and num2Double who are DoublePropertys and that make bind in num1 and num2 respectively, and, have done something equivalent to resultado.bind(num1Double.divide(num2Double));, that is, a gambiarra for something that should be very simple and already foreseen in Javafx.

So how do you do "the right way" in Javafx?

2 answers

1


I got a good answer on Soen here (link in English), below is a translation/adaptation of it:

The methods provided by LongProperty (and by others NumberExpression) as divide(...) are only methods of convenience; but you can create a Binding customized to do whatever you want:

final DoubleProperty result = new SimpleDoubleProperty(0.0);
final LongProperty longProperty1 = new SimpleLongProperty(812323534);
final LongProperty longProperty2 = new SimpleLongProperty(956745323);
result.bind(Bindings.createDoubleBinding(() -> longProperty1.longValue() / (double) longProperty2.longValue(),
    longProperty1, longProperty2));
System.out.println(result.get());
longProperty1.set(612323534);
System.out.println(result.get());

Bindings is a utility class to create bindings. Here I created a custom Binding that returns a double making cast of the division of longs to double, but there is a loss of precision.

The exit code above is:

0.8490488685670847
0.6400068223798362

Through this answer, I get a Doublebiding created by createDoubleBinding(...) that will keep resultalways updated with the result of the longProperty1for longProperty2.

That is, when the value of either of these two variables is changed (either from longProperty1 or of longProperty2), the division will be automatically reprocessed and resultwill be updated automatically with the new split result.

See the example below:

final DoubleProperty result = new SimpleDoubleProperty(0.0);
final LongProperty longProperty1 = new SimpleLongProperty(45);
final LongProperty longProperty2 = new SimpleLongProperty(7);

result.bind(Bindings.createDoubleBinding(() -> longProperty1.longValue() / (double) longProperty2.longValue(),
            longProperty1, longProperty2));

System.out.println(result.get()); // imprime "6.428571428571429", que é 45 dividido por 7

longProperty1.set(100); // longProperty1 agora contém o valor 100
System.out.println(result.get()); // imprime "14.285714285714286", que é 100 dividido por 7

longProperty2.set(876); // longProperty2 agora contém o valor 876
System.out.println(result.get()); // imprime "0.1141552511415525", que é 100 dividido por 876

Note that the decimal places are preserved as I need them, and even if the accuracy of the result is affected by the time the division is made, I can still store giant numbers (long) inside longProperty1 and longProperty2 without them being affected, it is not necessary to transform them into DoublePropertys.

1

I have honestly never used this Java FX API, but I have experienced the same "problem" in several different Apis and languages.

Basically, what seems to occur is that when you work with whole types, the split is whole too.

Note that I wrote "problem" in quotes above because it is more of a feature than a bug. Some languages or Apis "upgrade" from integer to decimal automatically, others not, with advantages and disadvantages for each approach.

In your case, I’d say change SimpleLongProperty for SimpleDoubleProperty for your variables num1 and num2 would be a reasonable way to solve the problem, in the sense that you are normalizing your variables using decimal types rather than mixing decimals and integers.

  • 1

    My resistance to change num1 and num2 for DoublePropertys is because - most likely - they will receive very large numbers (long same), so it is not interesting for me to even cast them for double. On the other hand, result will receive a value of 0.0 until 1.0, because in my real problem, num1 shall always be less than or equal to num2.

  • @Douglas I understand your concern, but Double is not smaller than Long. She has a limitation, but can generally accommodate Long numbers without problems. It doesn’t get to be something like Bigdecimal, which unfortunately doesn’t seem to be part of this API (I did a brief search in Javadoc), but it should be enough for most use cases.

  • 1

    utluiz take a look at reply that I just posted (obtained from Soen), I believe it is the best way that Javafx offers to do what I asked.

  • @Douglas I really can’t judge what the best answer would be. Basically you’re turning your long variables into double underneath the scenes, so it basically has no difference in numerical accuracy. The advantage is that you keep your input variables in the type you want, the disadvantage is that your code gets more polluted.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.