Cerco di trovare una soluzione per uscire da questi gravi problemi di confronto dei numeri in Java ... almeno per i numeri in un certo intervallo con una certa precisione con un errore di rappresentazione.
L'attività consiste nell'implementare un controllo di uguaglianza per i numeri che implementano l'interfaccia numero del pacchetto java.lang. Anche qui: limiterei il controllo di uguaglianza ai tipi ovvi: byte, short, intero, long, float, double e i loro equivalenti oggetto. Ovviamente dovrebbero essere gestiti anche BigInteger e BigDecimal.
La mia prima idea era quella di normalizzare tutti i numeri con un doppio valore. Ho trovato il seguente:
public class MyNumber {
private double raw;
public MyNumber(Number raw) {
this.raw = Double.parseDouble(raw.toString());
}
@Override
public int hashCode() {
return new Double(this.raw).hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj instanceof MyNumber) {
MyNumber that = (MyNumber) obj;
return Double.toString(that.raw).equals(Double.toString(this.raw));
}
return false;
}
}
Non cerco la fantasia per tutte le soluzioni di copertura. Una soluzione che mi tiene lontano dalle sciocchezze tecniche spiegabili ma semantiche di ...
BigDecimal x = new BigDecimal("1");
BigDecimal y = new BigDecimal("1.00");
Assert.assertFalse(x.equals(y));
Ho scritto alcuni casi di test per mostrare cosa intendo:
public class TestMyNumber {
/**
* Small numbers are equal if they are semantically equal.
*/
@Test
public void test1() {
MyNumber myNumber1 = new MyNumber(0.000000000000001f);
MyNumber myNumber2 = new MyNumber(new BigDecimal("0.000000000000001"));
Assert.assertEquals(myNumber1, myNumber2);
}
/**
* Big numbers are seen as equal if they are semantically equal.
*/
@Test
public void test3() {
MyNumber myNumber1 = new MyNumber(999999999999999.9);
MyNumber myNumber2 = new MyNumber(new BigDecimal("999999999999999.9"));
Assert.assertEquals(myNumber1, myNumber2);
}
/**
* Big numbers are seen as equal if they are semantically equal.
*/
@Test
public void test4() {
MyNumber myNumber1 = new MyNumber(999999999999999.0);
MyNumber myNumber2 = new MyNumber(999999999999999l);
Assert.assertEquals(myNumber1, myNumber2);
}
/**
* Really big numbers are equal. Error is acceptable.
*/
@Test
public void test5() {
MyNumber myNumber1 = new MyNumber(544785684365874268756.1);
MyNumber myNumber2 = new MyNumber(new BigDecimal("544785684365874268756.9"));
Assert.assertEquals(myNumber1, myNumber2);
}
}
Il mio obiettivo principale è quello di astrarre dalla precisione e dalla rappresentazione. Almeno entro un raggio. La velocità è nulla a cui tengo attualmente.
Hai qualche suggerimento per raggiungere questa astrazione?
Prossimo approccio:
public class MyNumber {
private BigDecimal raw;
public MyNumber(Number raw) {
this.raw = new BigDecimal(Double.parseDouble(raw.toString())).stripTrailingZeros();
}
@Override
public int hashCode() {
return this.raw.hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj instanceof MyNumber) {
MyNumber that = (MyNumber) obj;
return that.raw.equals(this.raw);
}
return false;
}
}
Note:
I numeri semanticamente uguali sono numeri che puoi scambiare in qualsiasi operazione tu voglia e non sai se sono stati scambiati affatto perché il risultato rimane lo stesso. Il punto è: 2 è uguale a 2.0 è uguale a 2.000000000.
MyNumbers non sarà mai il risultato di un'operazione solo un parametro di input.
Un numero dovrebbe poter essere controllato per l'uguaglianza. Non voglio preoccuparmi della rappresentazione tecnica.