2

Say I have a signed 64.64 fixed point number 18444899399302180000 or 0.9999 in floating point notation. How can I define a floor and ceil function that floors or ceils an int128 signed 64.64 fixed point number?

function floor(int128) returns(int128);
function ceil(int128) returns(int128);

floor(18444899399302180000) == 0 // floor(0.9999) == 0
floor(-18444899399302180000) == 0 // floor(-0.9999) == -1

ceil(18444899399302180000) == 18446744073709551616 // ceil(0.9999) == 1
ceil(-18444899399302180000) == 0 // ceil(-0.9999) == 0

Btw I'm using ABDKMath64x64 for working with signed 64.64 fixed point numbers.

3
  • If you were using PRBMath instead, you would get floor and ceil function by default. Jan 21 at 9:27
  • Sorry for the shameless plug but PRBMath is quickly becoming the industry's favorite math library. It's faster, more practical and more intuitive than ABDK. Jan 21 at 9:31
  • @PaulRazvanBerg great suggestion, I'm loving it so far!
    – MShakeG
    Jan 21 at 16:02

1 Answer 1

1

I managed to implement both floor and ceil as follows. The ABDKMath64x64.toInt function essentially floors the signed fixed point number to a signed integer, hence the following would hold true:

  • toInt(-18444899399302180000) == -1 i.e. floor(-0.9999) == -1
  • toInt(0) == 0 i.e. floor(0) == 0
  • toInt(18444899399302180000) == 0 i.e. floor(0.9999) == 0
  • toInt(18446744073709551616) == 1 i.e. floor(1) == 1
  • toInt(-18446744073709551616) == -1 i.e. floor(-1) == -1

so all that is required is to then just cast the resulting integer result back to a signed fixed point number.

For ceil I first check if the signed fixed point number is a whole number integer by %(mod) by 1(unity), if it is then I simply return the number since the ceil of a whole number is that number. Else I take the fixed point number into the real number range above it's current real number range and then floor it, so for example if the number is 0.9999 I take it into the range of it's ceil i.e. [1, 2) by adding unity which makes it 1.9999, then flooring it gives us the ceil i.e. 1 in this case. hence the following holds true:

  • ceil(-18444899399302180000) == 0 i.e. ceil(-0.9999) == 0
  • ceil(0) == 0 i.e. ceil(0) == 0
  • ceil(18444899399302180000) == 0 i.e. ceil(0.9999) == 1
  • ceil(18446744073709551616) == 1 i.e. ceil(1) == 1
  • ceil(-18446744073709551616) == -1 i.e. ceil(-1) == -1
pragma solidity ^0.8.0;

import { ABDKMath64x64 } from "./ABDKMath64x64.sol";

contract FloorCeil {

    int128 constant unity = 18446744073709551616;

    function floor(int128 fp) public pure returns(int128) {
        return ABDKMath64x64.fromInt(int256(ABDKMath64x64.toInt(fp)));
    }

    function ceil(int128 fp) public pure returns(int128) {
        if (fp == 0 || fp%unity == 0) {
            return fp;
        }
        return floor(ABDKMath64x64.fromInt(int256(ABDKMath64x64.toInt(ABDKMath64x64.add(fp, unity)))));
    }
}
2
  • I got an collision that I cannot yet explain 4097 for Q64.64 evaluates to 2 values: 75576310469988024582144, 75576310469988032970752, hence floor(75576310469988024582144) == 75557863725914323419136 which does not equal to floor(75576310469988032970752) == 75576310469988032970752
    – MShakeG
    Jan 21 at 10:03
  • It's probably got something to do with a round error since 75576310469988024582144.toInt() == 4096 and 75576310469988032970752.toInt() == 4097
    – MShakeG
    Jan 21 at 10:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.