Solidity Math – How to Floor or Ceil a Fixed Point Number

fixed-pointmathsolidity

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.

Best Answer

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)))));
    }
}
Related Topic