Merge pull request #565 from VoR0220/fixedDataType

Fixed data typename fixes and documentation
This commit is contained in:
chriseth 2016-05-20 16:48:50 +02:00
commit 2f37356c58
3 changed files with 114 additions and 22 deletions

View File

@ -52,7 +52,7 @@ Operators:
* Arithmetic operators: `+`, `-`, unary `-`, unary `+`, `*`, `/`, `%` (remainder), `**` (exponentiation)
Division always truncates (it just maps to the DIV opcode of the EVM), but it does not truncate if both
operators are :ref:`literals<integer_literals>` (or literal expressions).
operators are :ref:`literals<rational_literals>` (or literal expressions).
.. index:: address, balance, send, call, callcode, delegatecall
@ -135,20 +135,60 @@ As a rule of thumb, use `bytes` for arbitrary-length raw byte data and `string`
for arbitrary-length string (utf-8) data. If you can limit the length to a certain
number of bytes, always use one of `bytes1` to `bytes32` because they are much cheaper.
.. index:: literal, literal;integer
.. index:: ! ufixed, ! fixed, ! fixed point number
.. _integer_literals:
Fixed Point Numbers
-------------------
Integer Literals
-----------------
**bold** COMING SOON... **bold**
Integer Literals are arbitrary precision integers until they are used together with a non-literal. In `var x = 1 - 2;`, for example, the value of `1 - 2` is `-1`, which is assigned to `x` and thus `x` receives the type `int8` -- the smallest type that contains `-1`, although the natural types of `1` and `2` are actually `uint8`.
.. index:: literal, literal;rational
It is even possible to temporarily exceed the maximum of 256 bits as long as only integer literals are used for the computation: `var x = (0xffffffffffffffffffff * 0xffffffffffffffffffff) * 0;` Here, `x` will have the value `0` and thus the type `uint8`.
.. _rational_literals:
Rational and Integer Literals
-----------------------------
All number literals retain arbitrary precision until they are converted to a non-literal type (i.e. by
using them together with a non-literal type). This means that computations do not overflow but also
divisions do not truncate.
For example, `(2**800 + 1) - 2**800` results in the constant `1` (of type `uint8`)
although intermediate results would not even fit the machine word size. Furthermore, `.5 * 8` results
in the integer `4` (although non-integers were used in between).
If the result is not an integer,
an appropriate `ufixed` or `fixed` type is used whose number of fractional bits is as large as
required (approximating the rational number in the worst case).
In `var x = 1/4;`, `x` will receive the type `ufixed0x8` while in `var x = 1/3` it will receive
the type `ufixed0x256` because `1/3` is not finitely representable in binary and will thus be
approximated.
Any operator that can be applied to integers can also be applied to literal expressions as
long as the operators are integers. If any of the two is fractional, bit operations are disallowed
and exponentiation is disallowed if the exponent is fractional (because that might result in
a non-rational number).
.. note::
Most finite decimal fractions like `5.3743` are not finitely representable in binary. The correct type
for `5.3743` is `ufixed8x248` because that allows to best approximate the number. If you want to
use the number together with types like `ufixed` (i.e. `ufixed128x128`), you have to explicitly
specify the desired precision: `x + ufixed(5.3743)`.
.. warning::
Divison on integer literals used to truncate in earlier versions, but it will actually convert into a rational number in the future, i.e. `1/2` is not equal to `0`, but to `0.5`.
Division on integer literals used to truncate in earlier versions, but it will now convert into a rational number, i.e. `5 / 2` is not equal to `2`, but to `2.5`.
.. note::
Literal expressions are converted to a permanent type as soon as they are used with other
expressions. Even though we know that the value of the
expression assigned to `b` in the following example evaluates to an integer, it still
uses fixed point types (and not rational number literals) in between and so the code
does not compile
::
uint128 a = 1;
uint128 b = 2.5 + a + 0.5;
.. index:: literal, literal;string, string

View File

@ -290,7 +290,8 @@ bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const
return _convertTo.category() == category() ||
_convertTo.category() == Category::Contract ||
_convertTo.category() == Category::Enum ||
_convertTo.category() == Category::FixedBytes;
_convertTo.category() == Category::FixedBytes ||
_convertTo.category() == Category::FixedPoint;
}
TypePointer IntegerType::unaryOperatorResult(Token::Value _operator) const
@ -328,10 +329,13 @@ string IntegerType::toString(bool) const
TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const
{
if (_other->category() != Category::RationalNumber && _other->category() != category())
if (
_other->category() != Category::RationalNumber &&
_other->category() != Category::FixedPoint &&
_other->category() != category()
)
return TypePointer();
auto commonType = dynamic_pointer_cast<IntegerType const>(Type::commonType(shared_from_this(), _other));
auto commonType = Type::commonType(shared_from_this(), _other); //might be a integer or fixed point
if (!commonType)
return TypePointer();
@ -341,9 +345,14 @@ TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointe
if (Token::isBooleanOp(_operator))
return TypePointer();
// Nothing else can be done with addresses
if (commonType->isAddress())
return TypePointer();
if (auto intType = dynamic_pointer_cast<IntegerType const>(commonType))
{
if (intType->isAddress())
return TypePointer();
}
else if (auto fixType = dynamic_pointer_cast<FixedPointType const>(commonType))
if (Token::Exp == _operator)
return TypePointer();
return commonType;
}
@ -386,7 +395,6 @@ bool FixedPointType::isImplicitlyConvertibleTo(Type const& _convertTo) const
else
return !convertTo.isSigned() || (convertTo.m_integerBits > m_integerBits);
}
return false;
}
@ -432,12 +440,12 @@ string FixedPointType::toString(bool) const
TypePointer FixedPointType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const
{
if (
_other->category() != Category::RationalNumber
&& _other->category() != category()
&& _other->category() != Category::Integer
_other->category() != Category::RationalNumber &&
_other->category() != category() &&
_other->category() != Category::Integer
)
return TypePointer();
auto commonType = dynamic_pointer_cast<FixedPointType const>(Type::commonType(shared_from_this(), _other));
auto commonType = Type::commonType(shared_from_this(), _other); //might be fixed point or integer
if (!commonType)
return TypePointer();
@ -447,8 +455,14 @@ TypePointer FixedPointType::binaryOperatorResult(Token::Value _operator, TypePoi
return commonType;
if (Token::isBitOp(_operator) || Token::isBooleanOp(_operator))
return TypePointer();
if (Token::Exp == _operator)
return TypePointer();
if (auto fixType = dynamic_pointer_cast<FixedPointType const>(commonType))
{
if (Token::Exp == _operator)
return TypePointer();
}
else if (auto intType = dynamic_pointer_cast<IntegerType const>(commonType))
if (intType->isAddress())
return TypePointer();
return commonType;
}

View File

@ -3683,6 +3683,44 @@ BOOST_AUTO_TEST_CASE(zero_handling)
BOOST_CHECK(success(text));
}
BOOST_AUTO_TEST_CASE(integer_and_fixed_interaction)
{
char const* text = R"(
contract test {
function f() {
ufixed a = uint128(1) + ufixed(2);
}
}
)";
BOOST_CHECK(success(text));
}
BOOST_AUTO_TEST_CASE(signed_rational_modulus)
{
char const* text = R"(
contract test {
function f() {
fixed a = 0.42578125 % -0.4271087646484375;
fixed b = .5 % a;
fixed c = a % b;
}
}
)";
BOOST_CHECK(success(text));
}
BOOST_AUTO_TEST_CASE(one_divided_by_three_integer_conversion)
{
char const* text = R"(
contract test {
function f() {
uint a = 1/3;
}
}
)";
BOOST_CHECK(!success(text));
}
BOOST_AUTO_TEST_SUITE_END()
}