Modified AssetAmount#multiply() and AssetAmount#divide() to not cast its value to a long primitive and thus preventing some overflow scenarios that the idea of using the UnsignedLong class is suposed to prevent

This commit is contained in:
Nelson R. Perez 2017-11-23 22:58:22 -05:00
parent 805903bed4
commit 9a1808d127
2 changed files with 34 additions and 4 deletions

View file

@ -17,6 +17,7 @@ import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.RoundingMode;
import cy.agorise.graphenej.errors.IncompatibleOperation;
@ -91,7 +92,9 @@ public class AssetAmount implements ByteSerializable, JsonSerializable {
* @return The same AssetAmount instance, but with the changed amount value.
*/
public AssetAmount multiplyBy(double factor, RoundingMode roundingMode){
this.amount = UnsignedLong.valueOf(DoubleMath.roundToLong(this.amount.longValue() * factor, roundingMode));
BigDecimal originalAmount = new BigDecimal(amount.bigIntegerValue());
BigDecimal decimalResult = originalAmount.multiply(new BigDecimal(factor));
this.amount = UnsignedLong.valueOf(DoubleMath.roundToBigInteger(decimalResult.doubleValue(), roundingMode));
return this;
}
@ -111,7 +114,9 @@ public class AssetAmount implements ByteSerializable, JsonSerializable {
* @return: The same AssetAMount instance, but with the divided amount value
*/
public AssetAmount dividedBy(double divisor, RoundingMode roundingMode){
this.amount = UnsignedLong.valueOf(DoubleMath.roundToLong(this.amount.longValue() / divisor, roundingMode));
BigDecimal originalAmount = new BigDecimal(amount.bigIntegerValue());
BigDecimal decimalAmount = originalAmount.divide(new BigDecimal(divisor), 18, RoundingMode.HALF_UP);
this.amount = UnsignedLong.valueOf(DoubleMath.roundToBigInteger(decimalAmount.doubleValue(), roundingMode));
return this;
}

View file

@ -1,13 +1,14 @@
package cy.agorise.graphenej;
import com.google.common.primitives.UnsignedLong;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
/**
* Created by nelson on 2/23/17.
* Testing AssetAmount operations.
*/
public class AssetAmountTest {
private final int LARGE_VALUE = 1000;
@ -27,4 +28,28 @@ public class AssetAmountTest {
assertEquals(large.subtract(small).getAmount(), new AssetAmount(UnsignedLong.valueOf(LARGE_VALUE - SMALL_VALUE), testAsset).getAmount());
assertEquals(small.subtract(large).getAmount(), new AssetAmount(UnsignedLong.valueOf(Math.abs(SMALL_VALUE - LARGE_VALUE)), testAsset).getAmount());
}
@Test
public void testMultiplication(){
// Testing a simple multiplication by a double
AssetAmount result = large.multiplyBy(0.5);
assertEquals(500, result.getAmount().longValue());
// Testing the multiplication of a number that would normally give an overflow
AssetAmount max = new AssetAmount(UnsignedLong.valueOf(Long.MAX_VALUE), testAsset);
AssetAmount overMaxLong = max.multiplyBy(1.5);
assertEquals("13835058055282163712", overMaxLong.getAmount().toString(10));
}
@Test
public void testDivision(){
// Testing a simple division by a double
AssetAmount result = large.dividedBy(0.5);
assertEquals(2000, result.getAmount().longValue());
// Testing a division of a number that would normally give an overflow
AssetAmount max = new AssetAmount(UnsignedLong.valueOf(Long.MAX_VALUE), testAsset);
AssetAmount overMaxLong = max.dividedBy(0.8);
assertEquals("11529215046068469760", overMaxLong.getAmount().toString());
}
}