From 30f4588562904215fa63779e06cb8ab5617bdbc0 Mon Sep 17 00:00:00 2001 From: 7400 <> Date: Mon, 13 Nov 2023 10:07:51 -0800 Subject: [PATCH 1/2] WIP: IEEE754 --- test/TestIEEE754.sol | 110 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 test/TestIEEE754.sol diff --git a/test/TestIEEE754.sol b/test/TestIEEE754.sol new file mode 100644 index 0000000..de4eb77 --- /dev/null +++ b/test/TestIEEE754.sol @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.0; +pragma abicoder v2; + +import "forge-std/Test.sol"; + +library IEEE754 { + + // reduce (signed)) int to p bits + + function bits2int (uint256 b, uint256 p) internal pure returns (int256 r) { + require(p >= 1 && p <= 256, "p invalid"); + uint256 s = 256 - p; + r = int256( b << s ) >> s; + } + + int32 internal constant EBIAS = 127; + int32 internal constant ENAN = 0xff; // Also includes infinity + int32 internal constant ESUBNORM = 0; + + uint32 internal constant SIGN_MASK = 0x8000_0000; + uint32 internal constant EXP_MASK = 0x7f80_0000; + uint32 internal constant MANT_MASK = 0x007f_ffff; + uint32 internal constant ZERO_MASK = EXP_MASK | MANT_MASK; + int32 internal constant MSB = 0x0080_0000; + + // Todo rounding + + function float2fixed(uint32 floatingPoint, uint256 fixedBits) internal pure returns (int256 fixedPoint) {unchecked{ + + // Zero case + + if (floatingPoint & ZERO_MASK == 0) return 0; + + // Extract exponent field + + int32 exp = int32(floatingPoint & EXP_MASK) >> 23; + require (exp != ENAN, "NaN not supported"); + + // Extract mantisa + + int256 mant = int32(floatingPoint & MANT_MASK); + if (exp != ESUBNORM) mant |= MSB; // Add implied MSB to non-subnormal + if (floatingPoint & SIGN_MASK != 0) mant = -mant; // Negate if sign bit set + + // Compute shift amount + + exp = exp - EBIAS; // Remove exponent bias + int256 rshft = 23 - int256(fixedBits) - exp; // Zero exp and integer fixedPoint throws away all but MSB + + // Shift to arrive at fixed point alignment + + if (rshft < 0) // Most likely case? + fixedPoint = mant << uint256(-rshft); + else if (rshft > 0) + fixedPoint = mant >> uint256(rshft); + else + fixedPoint = mant; + + return fixedPoint; + }} +} + +contract TestIEEE754 is Test { + + function setUp() external pure { + console2.log('TestFloat setup()'); + } + + // Useful constants + + uint256 constant internal umin = 0; + uint256 constant internal umax = ~umin; // 0xff... + int256 constant internal imax = int256(umax>>1); // 0x7f... + int256 constant internal imin = ~imax; // 0x80... + + // must be external because called as this.sanityReverts + + function sanityReverts(int256 t) external pure { + console2.log("sanityReverts", t); + uint256 u; + int256 i; + if (t == 1) { + u = umax+1; // Overflow + } else if (t == 2) { + u = umin-1; // Underflow + } else if (t == 3) { + i = imax+1; // Overflow + } else if (t == 4) { + i = imin-1; // Underflow + } else { + // Will not revert + } + } + + function testSanity() external { + console2.log('Float.testSanity()'); + int256 i; + for (i=1; i<=4; i++) { + vm.expectRevert(); + this.sanityReverts(i); // Convoluted way to check reverts + } + i = imax << 1; // Changes sign bit, but should not over/underflow + i = imin << 1; // Changes sign bit, but should not over/underflow + } + + function testFloat() external pure { + console2.log('Float.testFloat()'); + } +} From f706d562186878267f6c27559e7870ab24eea4fd Mon Sep 17 00:00:00 2001 From: 7400 <> Date: Mon, 13 Nov 2023 11:44:57 -0800 Subject: [PATCH 2/2] Basic IEEE754 tests --- test/TestIEEE754.sol | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/test/TestIEEE754.sol b/test/TestIEEE754.sol index de4eb77..1b10c40 100644 --- a/test/TestIEEE754.sol +++ b/test/TestIEEE754.sol @@ -26,7 +26,7 @@ library IEEE754 { // Todo rounding - function float2fixed(uint32 floatingPoint, uint256 fixedBits) internal pure returns (int256 fixedPoint) {unchecked{ + function float2fixed(uint32 floatingPoint, uint32 fixedBits) internal pure returns (int256 fixedPoint) {unchecked{ // Zero case @@ -46,7 +46,7 @@ library IEEE754 { // Compute shift amount exp = exp - EBIAS; // Remove exponent bias - int256 rshft = 23 - int256(fixedBits) - exp; // Zero exp and integer fixedPoint throws away all but MSB + int256 rshft = 23 - int32(fixedBits) - exp; // Zero exp and integer fixedPoint throws away all but MSB // Shift to arrive at fixed point alignment @@ -104,7 +104,30 @@ contract TestIEEE754 is Test { i = imin << 1; // Changes sign bit, but should not over/underflow } - function testFloat() external pure { - console2.log('Float.testFloat()'); + struct Item { + uint32 floatingPoint; + uint32 fixedBits; + int256 fixedPoint; + } + + function testFloat2fixed() external pure { + console2.log('TestIEEE754.testFloat2fixed()'); + + Item[6] memory items = [ + Item(0x3f800000, 0, 1 << 0), // 1.0 + Item(0x3f800000, 128, 1 << 128), // 1.0 + Item(0x3f800000, 254, 1 << 254), // 1.0 + Item(0xbf800000, 128, -1 << 128), // -1.0 + Item(0x40000000, 128, 2 << 128), // 1.0 + Item(0xc0000000, 128, -2 << 128) // 1.0 + // ,Item(0xbf800001, 128, -1 << 128) // Failing case for test debugging purposes + ]; + + for (uint i=0; i