aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorchriseth <c@ethdev.com>2016-05-20 22:48:50 +0800
committerchriseth <c@ethdev.com>2016-05-20 22:48:50 +0800
commit2f37356c589a912a2f1bc414faf8c5274b734673 (patch)
tree79506f5feda11bd0835f8c01cd4da58357269322
parent0d7ef65f6815df8eb5535bc9e9d81c6450d65623 (diff)
parent919235745263b36bdd7a88374b79f34214b14a5f (diff)
downloaddexon-solidity-2f37356c589a912a2f1bc414faf8c5274b734673.tar.gz
dexon-solidity-2f37356c589a912a2f1bc414faf8c5274b734673.tar.zst
dexon-solidity-2f37356c589a912a2f1bc414faf8c5274b734673.zip
Merge pull request #565 from VoR0220/fixedDataType
Fixed data typename fixes and documentation
-rw-r--r--docs/types.rst56
-rw-r--r--libsolidity/ast/Types.cpp42
-rw-r--r--test/libsolidity/SolidityNameAndTypeResolution.cpp38
3 files changed, 114 insertions, 22 deletions
diff --git a/docs/types.rst b/docs/types.rst
index 93e70ddb..d18a9490 100644
--- a/docs/types.rst
+++ b/docs/types.rst
@@ -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
diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp
index 0df68d3d..d989cb06 100644
--- a/libsolidity/ast/Types.cpp
+++ b/libsolidity/ast/Types.cpp
@@ -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;
}
diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp
index d7f7961a..bbc77d34 100644
--- a/test/libsolidity/SolidityNameAndTypeResolution.cpp
+++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp
@@ -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()
}