mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Fixed point types.
This commit is contained in:
parent
70cbd5d8fb
commit
f91e512f02
@ -553,8 +553,6 @@ BoolResult IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const
|
|||||||
return (!isSigned() && (numBits() == fixedBytesType->numBytes() * 8));
|
return (!isSigned() && (numBits() == fixedBytesType->numBytes() * 8));
|
||||||
else if (dynamic_cast<EnumType const*>(&_convertTo))
|
else if (dynamic_cast<EnumType const*>(&_convertTo))
|
||||||
return true;
|
return true;
|
||||||
else if (auto fixedPointType = dynamic_cast<FixedPointType const*>(&_convertTo))
|
|
||||||
return (isSigned() == fixedPointType->isSigned()) && (numBits() == fixedPointType->numBits());
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -893,6 +893,7 @@ void CompilerUtils::convertType(
|
|||||||
unsigned fractionalDigitsOnStack = 0;
|
unsigned fractionalDigitsOnStack = 0;
|
||||||
if (auto const* typeOnStack = dynamic_cast<IntegerType const*>(&_typeOnStack))
|
if (auto const* typeOnStack = dynamic_cast<IntegerType const*>(&_typeOnStack))
|
||||||
{
|
{
|
||||||
|
// TODO do we have to subtract the fractional digits?
|
||||||
if (targetFixedPointType.numBits() > typeOnStack->numBits())
|
if (targetFixedPointType.numBits() > typeOnStack->numBits())
|
||||||
cleanHigherOrderBits(*typeOnStack);
|
cleanHigherOrderBits(*typeOnStack);
|
||||||
}
|
}
|
||||||
|
@ -434,6 +434,7 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
|
|||||||
case Token::Inc: // ++ (pre- or postfix)
|
case Token::Inc: // ++ (pre- or postfix)
|
||||||
case Token::Dec: // -- (pre- or postfix)
|
case Token::Dec: // -- (pre- or postfix)
|
||||||
solAssert(!!m_currentLValue, "LValue not retrieved.");
|
solAssert(!!m_currentLValue, "LValue not retrieved.");
|
||||||
|
// TODO
|
||||||
solUnimplementedAssert(
|
solUnimplementedAssert(
|
||||||
type.category() != Type::Category::FixedPoint,
|
type.category() != Type::Category::FixedPoint,
|
||||||
"Not yet implemented - FixedPointType."
|
"Not yet implemented - FixedPointType."
|
||||||
@ -2265,7 +2266,30 @@ void ExpressionCompiler::appendOrdinaryBinaryOperatorCode(Token _operator, Type
|
|||||||
void ExpressionCompiler::appendArithmeticOperatorCode(Token _operator, Type const& _type)
|
void ExpressionCompiler::appendArithmeticOperatorCode(Token _operator, Type const& _type)
|
||||||
{
|
{
|
||||||
if (_type.category() == Type::Category::FixedPoint)
|
if (_type.category() == Type::Category::FixedPoint)
|
||||||
solUnimplemented("Not yet implemented - FixedPointType.");
|
{
|
||||||
|
bool checked = (m_context.arithmetic() == Arithmetic::Checked);
|
||||||
|
FixedPointType const& type = dynamic_cast<FixedPointType const&>(_type);
|
||||||
|
string functionName;
|
||||||
|
switch (_operator)
|
||||||
|
{
|
||||||
|
case Token::Add:
|
||||||
|
functionName = m_context.utilFunctions().fixedAddFunction(type, checked);
|
||||||
|
break;
|
||||||
|
case Token::Sub:
|
||||||
|
functionName = m_context.utilFunctions().fixedSubFunction(type, checked);
|
||||||
|
break;
|
||||||
|
case Token::Mul:
|
||||||
|
functionName = m_context.utilFunctions().fixedMulFunction(type, checked);
|
||||||
|
break;
|
||||||
|
case Token::Div:
|
||||||
|
functionName = m_context.utilFunctions().fixedDivFunction(type, checked);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
solAssert(false, "Unknown arithmetic operator.");
|
||||||
|
}
|
||||||
|
m_context.callYulFunction(functionName, 2, 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
|
IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
|
||||||
if (m_context.arithmetic() == Arithmetic::Checked)
|
if (m_context.arithmetic() == Arithmetic::Checked)
|
||||||
|
@ -1139,6 +1139,108 @@ string YulUtilFunctions::wrappingIntExpFunction(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string YulUtilFunctions::fixedAddFunction(FixedPointType const& _type, bool _checked)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
_checked ?
|
||||||
|
overflowCheckedIntAddFunction(*_type.asIntegerType()) :
|
||||||
|
wrappingIntAddFunction(*_type.asIntegerType());
|
||||||
|
}
|
||||||
|
|
||||||
|
string YulUtilFunctions::fixedSubFunction(FixedPointType const& _type, bool _checked)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
_checked ?
|
||||||
|
overflowCheckedIntSubFunction(*_type.asIntegerType()) :
|
||||||
|
wrappingIntSubFunction(*_type.asIntegerType());
|
||||||
|
}
|
||||||
|
|
||||||
|
string YulUtilFunctions::fixedMulFunction(FixedPointType const& _type, bool _checked)
|
||||||
|
{
|
||||||
|
// TODO how does truncation behave for negative numbers?
|
||||||
|
solUnimplementedAssert(_type.numBits() <= 128, "Multiplication only implemented for up to 128 bits.");
|
||||||
|
|
||||||
|
// TODO This is exactly the same as integer mul, but with maxValue being
|
||||||
|
// maxValue * mulitplier and re-scaling at the end
|
||||||
|
// This only works for up to 128 bits.
|
||||||
|
string functionName = "fixed_mul_" + _type.identifier() + (_checked ? "_checked" : "");
|
||||||
|
return m_functionCollector.createFunction(functionName, [&]() {
|
||||||
|
u256 multiplier = pow(u256(10), _type.fractionalDigits());
|
||||||
|
return
|
||||||
|
// Multiplication by zero could be treated separately and directly return zero.
|
||||||
|
Whiskers(R"(
|
||||||
|
function <functionName>(x, y) -> product {
|
||||||
|
x := <cleanupFunction>(x)
|
||||||
|
y := <cleanupFunction>(y)
|
||||||
|
<?signed>
|
||||||
|
// overflow, if x > 0, y > 0 and x > (maxValue / y)
|
||||||
|
if and(and(sgt(x, 0), sgt(y, 0)), gt(x, div(<maxValue>, y))) { <panic>() }
|
||||||
|
// underflow, if x > 0, y < 0 and y < (minValue / x)
|
||||||
|
if and(and(sgt(x, 0), slt(y, 0)), slt(y, sdiv(<minValue>, x))) { <panic>() }
|
||||||
|
// underflow, if x < 0, y > 0 and x < (minValue / y)
|
||||||
|
if and(and(slt(x, 0), sgt(y, 0)), slt(x, sdiv(<minValue>, y))) { <panic>() }
|
||||||
|
// overflow, if x < 0, y < 0 and x < (maxValue / y)
|
||||||
|
if and(and(slt(x, 0), slt(y, 0)), slt(x, sdiv(<maxValue>, y))) { <panic>() }
|
||||||
|
<!signed>
|
||||||
|
// overflow, if x != 0 and y > (maxValue / x)
|
||||||
|
if and(iszero(iszero(x)), gt(y, div(<maxValue>, x))) { <panic>() }
|
||||||
|
</signed>
|
||||||
|
product := mul(x, y)
|
||||||
|
product := <?signed>sdiv<!signed>div</signed>(product, <multiplier>)
|
||||||
|
}
|
||||||
|
)")
|
||||||
|
("functionName", functionName)
|
||||||
|
("signed", _type.isSigned())
|
||||||
|
("maxValue", formatNumber(u256(_type.asIntegerType()->maxValue()) * multiplier))
|
||||||
|
("minValue", formatNumber(u256(_type.asIntegerType()->minValue()) * multiplier))
|
||||||
|
("cleanupFunction", cleanupFunction(_type))
|
||||||
|
("panic", panicFunction(PanicCode::UnderOverflow))
|
||||||
|
("multiplier", formatNumber(multiplier))
|
||||||
|
.render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
string YulUtilFunctions::fixedDivFunction(FixedPointType const& _type, bool _checked)
|
||||||
|
{
|
||||||
|
// TODO how does truncation behave for negative numbers?
|
||||||
|
solUnimplementedAssert(_type.numBits() <= 128, "Division only implemented for up to 128 bits.");
|
||||||
|
|
||||||
|
// TODO this is similar to int div in the same way as fixed mul is similar to int mul.
|
||||||
|
// TODO There more overflow cases than just `<min> / -1`
|
||||||
|
string functionName = "fixed_div_" + _type.identifier() + (_checked ? "_checked" : "");
|
||||||
|
return m_functionCollector.createFunction(functionName, [&]() {
|
||||||
|
u256 multiplier = pow(u256(10), _type.fractionalDigits());
|
||||||
|
return
|
||||||
|
Whiskers(R"(
|
||||||
|
function <functionName>(x, y) -> r {
|
||||||
|
x := <cleanupFunction>(x)
|
||||||
|
y := <cleanupFunction>(y)
|
||||||
|
if iszero(y) { <panicDivZero>() }
|
||||||
|
<?signed>
|
||||||
|
<?checked>
|
||||||
|
// overflow for minVal / -1
|
||||||
|
if and(
|
||||||
|
eq(x, <minVal>),
|
||||||
|
eq(y, mul(sub(0, 1), <multiplier>))
|
||||||
|
) { <panicOverflow>() }
|
||||||
|
</checked>
|
||||||
|
</signed>
|
||||||
|
r := <?signed>sdiv<!signed>div</signed>(mul(x, <multiplier>), y)
|
||||||
|
}
|
||||||
|
)")
|
||||||
|
("functionName", functionName)
|
||||||
|
("signed", _type.isSigned())
|
||||||
|
("checked", _checked)
|
||||||
|
("minValue", formatNumber(u256(_type.asIntegerType()->minValue())))
|
||||||
|
("cleanupFunction", cleanupFunction(_type))
|
||||||
|
("panicDivZero", panicFunction(PanicCode::DivisionByZero))
|
||||||
|
("panicOverflow", panicFunction(PanicCode::UnderOverflow))
|
||||||
|
("multiplier", formatNumber(multiplier))
|
||||||
|
.render();
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
string YulUtilFunctions::arrayLengthFunction(ArrayType const& _type)
|
string YulUtilFunctions::arrayLengthFunction(ArrayType const& _type)
|
||||||
{
|
{
|
||||||
string functionName = "array_length_" + _type.identifier();
|
string functionName = "array_length_" + _type.identifier();
|
||||||
|
@ -202,6 +202,18 @@ public:
|
|||||||
/// signature: (base, exponent) -> power
|
/// signature: (base, exponent) -> power
|
||||||
std::string wrappingIntExpFunction(IntegerType const& _type, IntegerType const& _exponentType);
|
std::string wrappingIntExpFunction(IntegerType const& _type, IntegerType const& _exponentType);
|
||||||
|
|
||||||
|
/// signature: (x, y) -> sum
|
||||||
|
std::string fixedAddFunction(FixedPointType const& _type, bool _checked);
|
||||||
|
|
||||||
|
/// signature: (x, y) -> difference
|
||||||
|
std::string fixedSubFunction(FixedPointType const& _type, bool _checked);
|
||||||
|
|
||||||
|
/// signature: (x, y) -> product
|
||||||
|
std::string fixedMulFunction(FixedPointType const& _type, bool _checked);
|
||||||
|
|
||||||
|
/// signature: (x, y) -> quotient
|
||||||
|
std::string fixedDivFunction(FixedPointType const& _type, bool _checked);
|
||||||
|
|
||||||
/// @returns the name of a function that fetches the length of the given
|
/// @returns the name of a function that fetches the length of the given
|
||||||
/// array
|
/// array
|
||||||
/// signature: (array) -> length
|
/// signature: (array) -> length
|
||||||
|
@ -797,6 +797,8 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp)
|
|||||||
bool isSigned = false;
|
bool isSigned = false;
|
||||||
if (auto type = dynamic_cast<IntegerType const*>(commonType))
|
if (auto type = dynamic_cast<IntegerType const*>(commonType))
|
||||||
isSigned = type->isSigned();
|
isSigned = type->isSigned();
|
||||||
|
else if (auto type = dynamic_cast<FixedPointType const*>(commonType))
|
||||||
|
isSigned = type->isSigned();
|
||||||
|
|
||||||
string args =
|
string args =
|
||||||
expressionAsType(_binOp.leftExpression(), *commonType, true) +
|
expressionAsType(_binOp.leftExpression(), *commonType, true) +
|
||||||
@ -2726,32 +2728,52 @@ string IRGeneratorForStatements::binaryOperation(
|
|||||||
}
|
}
|
||||||
else if (TokenTraits::isArithmeticOp(_operator))
|
else if (TokenTraits::isArithmeticOp(_operator))
|
||||||
{
|
{
|
||||||
solUnimplementedAssert(
|
|
||||||
_type.category() != Type::Category::FixedPoint,
|
|
||||||
"Not yet implemented - FixedPointType."
|
|
||||||
);
|
|
||||||
IntegerType const* type = dynamic_cast<IntegerType const*>(&_type);
|
|
||||||
solAssert(type, "");
|
|
||||||
bool checked = m_context.arithmetic() == Arithmetic::Checked;
|
bool checked = m_context.arithmetic() == Arithmetic::Checked;
|
||||||
switch (_operator)
|
if (_type.category() == Type::Category::Integer)
|
||||||
{
|
{
|
||||||
case Token::Add:
|
IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
|
||||||
fun = checked ? m_utils.overflowCheckedIntAddFunction(*type) : m_utils.wrappingIntAddFunction(*type);
|
switch (_operator)
|
||||||
break;
|
{
|
||||||
case Token::Sub:
|
case Token::Add:
|
||||||
fun = checked ? m_utils.overflowCheckedIntSubFunction(*type) : m_utils.wrappingIntSubFunction(*type);
|
fun = checked ? m_utils.overflowCheckedIntAddFunction(type) : m_utils.wrappingIntAddFunction(type);
|
||||||
break;
|
break;
|
||||||
case Token::Mul:
|
case Token::Sub:
|
||||||
fun = checked ? m_utils.overflowCheckedIntMulFunction(*type) : m_utils.wrappingIntMulFunction(*type);
|
fun = checked ? m_utils.overflowCheckedIntSubFunction(type) : m_utils.wrappingIntSubFunction(type);
|
||||||
break;
|
break;
|
||||||
case Token::Div:
|
case Token::Mul:
|
||||||
fun = checked ? m_utils.overflowCheckedIntDivFunction(*type) : m_utils.wrappingIntDivFunction(*type);
|
fun = checked ? m_utils.overflowCheckedIntMulFunction(type) : m_utils.wrappingIntMulFunction(type);
|
||||||
break;
|
break;
|
||||||
case Token::Mod:
|
case Token::Div:
|
||||||
fun = m_utils.intModFunction(*type);
|
fun = checked ? m_utils.overflowCheckedIntDivFunction(type) : m_utils.wrappingIntDivFunction(type);
|
||||||
break;
|
break;
|
||||||
default:
|
case Token::Mod:
|
||||||
break;
|
fun = m_utils.intModFunction(type);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
solAssert(_type.category() == Type::Category::FixedPoint, "");
|
||||||
|
FixedPointType const& type = dynamic_cast<FixedPointType const&>(_type);
|
||||||
|
switch (_operator)
|
||||||
|
{
|
||||||
|
case Token::Add:
|
||||||
|
fun = m_utils.fixedAddFunction(type, checked);
|
||||||
|
break;
|
||||||
|
case Token::Sub:
|
||||||
|
fun = m_utils.fixedSubFunction(type, checked);
|
||||||
|
break;
|
||||||
|
case Token::Mul:
|
||||||
|
fun = m_utils.fixedMulFunction(type, checked);
|
||||||
|
break;
|
||||||
|
case Token::Div:
|
||||||
|
fun = m_utils.fixedDivFunction(type, checked);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
solAssert(false, "Unknown arithmetic operator.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
contract c {
|
||||||
|
function mul(fixed a) public pure returns (fixed c) {
|
||||||
|
return a * 1.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// mul(fixed128x18): 100 -> 0x96
|
@ -0,0 +1,7 @@
|
|||||||
|
contract c {
|
||||||
|
function mul(fixed a, fixed b) public pure returns (fixed c) {
|
||||||
|
return a * b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// mul(fixed128x18,fixed128x18): 200, 10000000000000000000000 -> 2000000
|
@ -0,0 +1,11 @@
|
|||||||
|
contract C {
|
||||||
|
fixed x = 1.7;
|
||||||
|
fixed immutable y = -1.8;
|
||||||
|
fixed public z = - 9.3;
|
||||||
|
fixed constant w = -3.4;
|
||||||
|
function f() public view returns (fixed, fixed, fixed, fixed) {
|
||||||
|
return (x, y, this.z(), w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// f() ->
|
Loading…
Reference in New Issue
Block a user