diff --git a/src/lib/implementation/LibDecimalFloatImplementation.sol b/src/lib/implementation/LibDecimalFloatImplementation.sol index 93a66a34..ff0a3f8b 100644 --- a/src/lib/implementation/LibDecimalFloatImplementation.sol +++ b/src/lib/implementation/LibDecimalFloatImplementation.sol @@ -280,7 +280,15 @@ library LibDecimalFloatImplementation { scale = 1e75; adjustExponent = 75; } - exponent = exponentA - exponentB - adjustExponent; + // The order of subtraction matters in edge cases. For non-negative + // exponentA, apply the adjust exponent first to move the value + // towards 0 before exponentB is applied. This reduces the chance of + // a transient overflow in the intermediate subtraction. + if (exponentA >= 0) { + exponent = exponentA - adjustExponent - exponentB; + } else { + exponent = exponentA - exponentB - adjustExponent; + } (signedCoefficient, exponent) = unabsUnsignedMulOrDivLossy( signedCoefficientA, @@ -763,7 +771,7 @@ library LibDecimalFloatImplementation { } if (initialExponent < exponent) { - revert ExponentOverflow(signedCoefficient, exponent); + revert ExponentOverflow(signedCoefficient, initialExponent); } return (signedCoefficient, exponent); diff --git a/test/src/lib/LibDecimalFloat.div.t.sol b/test/src/lib/LibDecimalFloat.div.t.sol index c841e0f5..211a7ade 100644 --- a/test/src/lib/LibDecimalFloat.div.t.sol +++ b/test/src/lib/LibDecimalFloat.div.t.sol @@ -40,4 +40,32 @@ contract LibDecimalFloatDivTest is Test { this.divExternal(a, b); } } + + function testDivByOneFloat(int224 signedCoefficient, int32 exponent) external pure { + exponent = int32(bound(exponent, int256(type(int32).min) + 65, int256(type(int32).max))); + Float float = LibDecimalFloat.packLossless(signedCoefficient, exponent); + int256 one = 1; + for (int256 oneExponent = 0; oneExponent >= -65; --oneExponent) { + Float result = LibDecimalFloat.div(float, LibDecimalFloat.packLossless(one, oneExponent)); + assertTrue(result.eq(float)); + if (oneExponent == -65) { + break; + } + one *= 10; + } + } + + function testDivByNegativeOneFloat(int224 signedCoefficient, int32 exponent) external pure { + exponent = int32(bound(exponent, int256(type(int32).min) + 65, int256(type(int32).max) - 65)); + Float float = LibDecimalFloat.packLossless(signedCoefficient, exponent); + int256 negativeOne = -1; + for (int256 oneExponent = 0; oneExponent >= -65; --oneExponent) { + Float result = LibDecimalFloat.div(float, LibDecimalFloat.packLossless(negativeOne, oneExponent)); + assertTrue(result.eq(float.minus())); + if (oneExponent == -65) { + break; + } + negativeOne *= 10; + } + } } diff --git a/test/src/lib/implementation/LibDecimalFloatImplementation.div.t.sol b/test/src/lib/implementation/LibDecimalFloatImplementation.div.t.sol index 1090eb93..eebc800a 100644 --- a/test/src/lib/implementation/LibDecimalFloatImplementation.div.t.sol +++ b/test/src/lib/implementation/LibDecimalFloatImplementation.div.t.sol @@ -100,6 +100,39 @@ contract LibDecimalFloatImplementationDivTest is Test { assertEq(exponent, -76); } + /// Should be possible to divide every number by 1. + function testDivBy1(int256 signedCoefficient, int256 exponent) external pure { + exponent = bound(exponent, type(int256).min + 76, type(int256).max); + (int256 expectedCoefficient, int256 expectedExponent) = + LibDecimalFloatImplementation.maximize(signedCoefficient, exponent); + + int256 one = 1; + for (int256 oneExponent = 0; oneExponent >= -76; --oneExponent) { + checkDiv(signedCoefficient, exponent, one, oneExponent, expectedCoefficient, expectedExponent); + if (oneExponent == -76) { + break; + } + one *= 10; + } + } + + function testDivByNegativeOneFloat(int256 signedCoefficient, int256 exponent) external pure { + exponent = bound(exponent, type(int256).min + 76, type(int256).max); + (int256 expectedCoefficient, int256 expectedExponent) = + LibDecimalFloatImplementation.maximize(signedCoefficient, exponent); + (expectedCoefficient, expectedExponent) = + LibDecimalFloatImplementation.minus(expectedCoefficient, expectedExponent); + + int256 negativeOne = -1; + for (int256 oneExponent = 0; oneExponent >= -76; --oneExponent) { + checkDiv(signedCoefficient, exponent, negativeOne, oneExponent, expectedCoefficient, expectedExponent); + if (oneExponent == -76) { + break; + } + negativeOne *= 10; + } + } + /// forge-config: default.fuzz.runs = 100 function testUnnormalizedThreesDiv0(int256 exponentA, int256 exponentB) external pure { exponentA = bound(exponentA, EXPONENT_MIN / 2, EXPONENT_MAX / 2);